From fac508c4cba7fb6f58c430f17ad8d6074d53e713 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 7 Mar 2024 17:22:28 +1000 Subject: [PATCH 01/79] Can translate between profiles. --- src/ImageSharp/ColorProfiles/CieConstants.cs | 21 +++ src/ImageSharp/ColorProfiles/CieLab.cs | 177 ++++++++++++++++++ src/ImageSharp/ColorProfiles/CieXyz.cs | 124 ++++++++++++ .../ColorProfiles/ColorConversionOptions.cs | 51 +++++ .../ColorProfiles/ColorProfileConverter.cs | 30 +++ ...rProfileConverterExtensionsCieLabCieXyz.cs | 52 +++++ ...rProfileConverterExtensionsCieXyzCieXyz.cs | 52 +++++ .../IColorProfile{TSelf,TProfileSpace}.cs | 47 +++++ .../ColorProfiles/IProfileConnectingSpace.cs | 18 ++ src/ImageSharp/ColorProfiles/Illuminants.cs | 71 +++++++ src/ImageSharp/ColorProfiles/Lms.cs | 136 ++++++++++++++ .../ColorProfiles/LmsAdaptationMatrix.cs | 132 +++++++++++++ .../VonKriesChromaticAdaptation.cs | 90 +++++++++ .../VonKriesChromaticAdaptation.cs | 2 +- .../ApproximateColorProfileComparer.cs | 42 +++++ .../CieXyzAndCieLabConversionTest.cs | 86 +++++++++ .../CieXyzAndLmsConversionTest.cs | 81 ++++++++ .../ImageSharp.Tests/ImageSharp.Tests.csproj | 2 +- 18 files changed, 1212 insertions(+), 2 deletions(-) create mode 100644 src/ImageSharp/ColorProfiles/CieConstants.cs create mode 100644 src/ImageSharp/ColorProfiles/CieLab.cs create mode 100644 src/ImageSharp/ColorProfiles/CieXyz.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorConversionOptions.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverter.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs create mode 100644 src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs create mode 100644 src/ImageSharp/ColorProfiles/IProfileConnectingSpace.cs create mode 100644 src/ImageSharp/ColorProfiles/Illuminants.cs create mode 100644 src/ImageSharp/ColorProfiles/Lms.cs create mode 100644 src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs create mode 100644 src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs diff --git a/src/ImageSharp/ColorProfiles/CieConstants.cs b/src/ImageSharp/ColorProfiles/CieConstants.cs new file mode 100644 index 000000000..f4b74eaa1 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieConstants.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Constants use for Cie conversion calculations +/// +/// +internal static class CieConstants +{ + /// + /// 216F / 24389F + /// + public const float Epsilon = 0.008856452F; + + /// + /// 24389F / 27F + /// + public const float Kappa = 903.2963F; +} diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs new file mode 100644 index 000000000..c06fe765c --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -0,0 +1,177 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents a CIE L*a*b* 1976 color. +/// +/// +public readonly struct CieLab : IProfileConnectingSpace +{ + /// + /// D50 standard illuminant. + /// Used when reference white is not specified explicitly. + /// + public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; + + /// + /// Initializes a new instance of the struct. + /// + /// The lightness dimension. + /// The a (green - magenta) component. + /// The b (blue - yellow) component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLab(float l, float a, float b) + : this(new Vector3(l, a, b)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l, a, b components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLab(Vector3 vector) + : this() + { + // Not clamping as documentation about this space only indicates "usual" ranges + this.L = vector.X; + this.A = vector.Y; + this.B = vector.Z; + } + + /// + /// Gets the lightness dimension. + /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white). + /// + public readonly float L { get; } + + /// + /// Gets the a color component. + /// A value usually ranging from -100 to 100. Negative is green, positive magenta. + /// + public readonly float A { get; } + + /// + /// Gets the b color component. + /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow + /// + public readonly float B { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(CieLab left, CieLab right) => left.Equals(right); + + /// + /// Compares two objects for inequality + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right); + + /// + public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B); + + /// + public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is CieLab other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieLab other) => + this.L.Equals(other.L) + && this.A.Equals(other.A) + && this.B.Equals(other.B); + + /// + [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); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span 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); + } + } + + /// + [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); + + // Avoids XYZ coordinates out range (restricted by 0 and XYZ reference white) + Vector3 xyzr = Vector3.Clamp(new Vector3(xr, yr, zr), Vector3.Zero, Vector3.One); + + return new(xyzr * wxyz); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + for (int i = 0; i < source.Length; i++) + { + CieLab lab = source[i]; + destination[i] = lab.ToProfileConnectingSpace(options); + } + } +} diff --git a/src/ImageSharp/ColorProfiles/CieXyz.cs b/src/ImageSharp/ColorProfiles/CieXyz.cs new file mode 100644 index 000000000..b22ab16f9 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieXyz.cs @@ -0,0 +1,124 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents an CIE XYZ 1931 color +/// +/// +public readonly struct CieXyz : IProfileConnectingSpace +{ + /// + /// Initializes a new instance of the struct. + /// + /// X is a mix (a linear combination) of cone response curves chosen to be nonnegative + /// The y luminance component. + /// Z is quasi-equal to blue stimulation, or the S cone of the human eye. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieXyz(float x, float y, float z) + : this(new Vector3(x, y, z)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the x, y, z components. + public CieXyz(Vector3 vector) + : this() + { + // Not clamping as documentation about this space only indicates "usual" ranges + this.X = vector.X; + this.Y = vector.Y; + this.Z = vector.Z; + } + + /// + /// Gets the X component. A mix (a linear combination) of cone response curves chosen to be nonnegative. + /// A value usually ranging between 0 and 1. + /// + public float X { get; } + + /// + /// Gets the Y luminance component. + /// A value usually ranging between 0 and 1. + /// + public float Y { get; } + + /// + /// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response. + /// A value usually ranging between 0 and 1. + /// + public float Z { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(CieXyz left, CieXyz right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(CieXyz left, CieXyz right) => !left.Equals(right); + + /// + /// Returns a new representing this instance. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 ToVector3() => new(this.X, this.Y, this.Z); + + /// + public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z); + + /// + public override string ToString() => FormattableString.Invariant($"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is CieXyz other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieXyz other) + => this.X.Equals(other.X) + && this.Y.Equals(other.Y) + && this.Z.Equals(other.Z); + + /// + public static CieXyz FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + => new(source.X, source.Y, source.Z); + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + source.CopyTo(destination[..source.Length]); + } + + /// + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + => new(this.X, this.Y, this.Z); + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + source.CopyTo(destination[..source.Length]); + } +} diff --git a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs new file mode 100644 index 000000000..de32aa54d --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Provides options for color profile conversion. +/// +public class ColorConversionOptions +{ + private Matrix4x4 adaptationMatrix; + + /// + /// Initializes a new instance of the class. + /// + public ColorConversionOptions() => this.AdaptationMatrix = LmsAdaptationMatrix.Bradford; + + /// + /// Gets the memory allocator. + /// + public MemoryAllocator MemoryAllocator { get; init; } = MemoryAllocator.Default; + + /// + /// Gets the source white point used for chromatic adaptation in conversions from/to XYZ color space. + /// + public CieXyz WhitePoint { get; init; } = Illuminants.D50; + + /// + /// Gets the destination white point used for chromatic adaptation in conversions from/to XYZ color space. + /// + public CieXyz TargetWhitePoint { get; init; } = Illuminants.D50; + + /// + /// Gets the transformation matrix used in conversion to perform chromatic adaptation. + /// + public Matrix4x4 AdaptationMatrix + { + get => this.adaptationMatrix; + init + { + this.adaptationMatrix = value; + Matrix4x4.Invert(value, out Matrix4x4 inverted); + this.InverseAdaptationMatrix = inverted; + } + } + + internal Matrix4x4 InverseAdaptationMatrix { get; private set; } +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs new file mode 100644 index 000000000..af9ab0af8 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs @@ -0,0 +1,30 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Allows the conversion of color profiles. +/// +public class ColorProfileConverter +{ + /// + /// Initializes a new instance of the class. + /// + public ColorProfileConverter() + : this(new()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The color profile conversion options. + public ColorProfileConverter(ColorConversionOptions options) + => this.Options = options; + + /// + /// Gets the color profile conversion options. + /// + public ColorConversionOptions Options { get; } +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs new file mode 100644 index 000000000..481280b85 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsCieLabCieXyz +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + CieLab pcsFrom = source.ToProfileConnectingSpace(options); + + // Convert between PCS + CieXyz pcsTo = pcsFrom.ToProfileConnectingSpace(options); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, in pcsTo); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFrom = pcsFromOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFrom); + + // Convert between PCS. + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length * 2); + Span pcsTo = pcsToOwner.GetSpan()[..source.Length]; + CieLab.ToProfileConnectionSpace(options, pcsFrom, pcsTo); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsTo, pcsTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsTo, destination); + } +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs new file mode 100644 index 000000000..ebe07003e --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsCieXyzCieXyz +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + CieXyz pcsFrom = source.ToProfileConnectingSpace(options); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + + // Convert between PCS + CieXyz pcsTo = CieXyz.FromProfileConnectingSpace(options, in pcsFrom); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFrom = pcsFromOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFrom); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + + // Convert between PCS. + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsTo = pcsToOwner.GetSpan(); + CieXyz.FromProfileConnectionSpace(options, pcsFrom, pcsTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsTo, destination); + } +} diff --git a/src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs b/src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs new file mode 100644 index 000000000..e1d4cae35 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Defines the contract for all color profiles. +/// +/// The type of color profile. +/// The type of color profile connecting space. +public interface IColorProfile : IEquatable + where TSelf : IColorProfile + where TProfileSpace : struct, IProfileConnectingSpace +{ + /// + /// Converts the color to the profile connection space. + /// + /// The color profile conversion options. + /// The . + public TProfileSpace ToProfileConnectingSpace(ColorConversionOptions options); + +#pragma warning disable CA1000 // Do not declare static members on generic types + /// + /// Converts the color from the profile connection space. + /// + /// The color profile conversion options. + /// The color profile connecting space. + /// The . + public static abstract TSelf FromProfileConnectingSpace(ColorConversionOptions options, in TProfileSpace source); + + /// + /// Converts the span of colors to the profile connection space. + /// + /// The color profile conversion options. + /// The color span to convert from. + /// The color profile span to write the results to. + public static abstract void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination); + + /// + /// Converts the span of colors from the profile connection space. + /// + /// The color profile conversion options. + /// The color profile span to convert from. + /// The color span to write the results to. + public static abstract void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination); +#pragma warning restore CA1000 // Do not declare static members on generic types +} diff --git a/src/ImageSharp/ColorProfiles/IProfileConnectingSpace.cs b/src/ImageSharp/ColorProfiles/IProfileConnectingSpace.cs new file mode 100644 index 000000000..2ac736f44 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/IProfileConnectingSpace.cs @@ -0,0 +1,18 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Defines the contract for all color profile connection spaces. +/// +public interface IProfileConnectingSpace; + +/// +/// Defines the contract for all color profile connection spaces. +/// +/// The type of color profile. +/// The type of color profile connecting space. +public interface IProfileConnectingSpace : IColorProfile, IProfileConnectingSpace + where TSelf : struct, IColorProfile, IProfileConnectingSpace + where TProfileSpace : struct, IProfileConnectingSpace; diff --git a/src/ImageSharp/ColorProfiles/Illuminants.cs b/src/ImageSharp/ColorProfiles/Illuminants.cs new file mode 100644 index 000000000..0295dc484 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Illuminants.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// The well known standard illuminants. +/// Standard illuminants provide a basis for comparing images or colors recorded under different lighting +/// +/// +/// Coefficients taken from: http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html +///
+/// Descriptions taken from: http://en.wikipedia.org/wiki/Standard_illuminant +///
+public static class Illuminants +{ + /// + /// Incandescent / Tungsten + /// + public static readonly CieXyz A = new(1.09850F, 1F, 0.35585F); + + /// + /// Direct sunlight at noon (obsoleteF) + /// + public static readonly CieXyz B = new(0.99072F, 1F, 0.85223F); + + /// + /// Average / North sky Daylight (obsoleteF) + /// + public static readonly CieXyz C = new(0.98074F, 1F, 1.18232F); + + /// + /// Horizon Light. ICC profile PCS + /// + public static readonly CieXyz D50 = new(0.96422F, 1F, 0.82521F); + + /// + /// Mid-morning / Mid-afternoon Daylight + /// + public static readonly CieXyz D55 = new(0.95682F, 1F, 0.92149F); + + /// + /// Noon Daylight: TelevisionF, sRGB color space + /// + public static readonly CieXyz D65 = new(0.95047F, 1F, 1.08883F); + + /// + /// North sky Daylight + /// + public static readonly CieXyz D75 = new(0.94972F, 1F, 1.22638F); + + /// + /// Equal energy + /// + public static readonly CieXyz E = new(1F, 1F, 1F); + + /// + /// Cool White Fluorescent + /// + public static readonly CieXyz F2 = new(0.99186F, 1F, 0.67393F); + + /// + /// D65 simulatorF, Daylight simulator + /// + public static readonly CieXyz F7 = new(0.95041F, 1F, 1.08747F); + + /// + /// Philips TL84F, Ultralume 40 + /// + public static readonly CieXyz F11 = new(1.00962F, 1F, 0.64350F); +} diff --git a/src/ImageSharp/ColorProfiles/Lms.cs b/src/ImageSharp/ColorProfiles/Lms.cs new file mode 100644 index 000000000..e11c1ca87 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Lms.cs @@ -0,0 +1,136 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal readonly struct Lms : IColorProfile +{ + /// + /// Initializes a new instance of the struct. + /// + /// L represents the responsivity at long wavelengths. + /// M represents the responsivity at medium wavelengths. + /// S represents the responsivity at short wavelengths. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Lms(float l, float m, float s) + : this(new Vector3(l, m, s)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l, m, s components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Lms(Vector3 vector) + { + // Not clamping as documentation about this space only indicates "usual" ranges + this.L = vector.X; + this.M = vector.Y; + this.S = vector.Z; + } + + /// + /// Gets the L long component. + /// A value usually ranging between -1 and 1. + /// + public readonly float L { get; } + + /// + /// Gets the M medium component. + /// A value usually ranging between -1 and 1. + /// + public readonly float M { get; } + + /// + /// Gets the S short component. + /// A value usually ranging between -1 and 1. + /// + public readonly float S { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Lms left, Lms right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Lms left, Lms right) => !left.Equals(right); + + /// + /// Returns a new representing this instance. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 ToVector3() => new(this.L, this.M, this.S); + + /// + public override int GetHashCode() => HashCode.Combine(this.L, this.M, this.S); + + /// + public override string ToString() => FormattableString.Invariant($"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is Lms other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Lms other) + => this.L.Equals(other.L) + && this.M.Equals(other.M) + && this.S.Equals(other.S); + + /// + public static Lms FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + { + Vector3 vector = Vector3.Transform(source.ToVector3(), options.AdaptationMatrix); + return new Lms(vector); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span 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); + } + } + + /// + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + { + Vector3 vector = Vector3.Transform(this.ToVector3(), options.InverseAdaptationMatrix); + return new CieXyz(vector); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + for (int i = 0; i < source.Length; i++) + { + Lms lms = source[i]; + destination[i] = lms.ToProfileConnectingSpace(options); + } + } +} diff --git a/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs b/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs new file mode 100644 index 000000000..70bc5aef2 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs @@ -0,0 +1,132 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Matrices used for transformation from to , defining the cone response domain. +/// +/// +/// Matrix 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 +/// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf +/// +public static class LmsAdaptationMatrix +{ + /// + /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65) + /// + public static readonly Matrix4x4 VonKriesHPEAdjusted + = Matrix4x4.Transpose(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. + }); + + /// + /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez for equal energy) + /// + public static readonly Matrix4x4 VonKriesHPE + = Matrix4x4.Transpose(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 + }); + + /// + /// XYZ scaling chromatic adaptation transform matrix + /// + public static readonly Matrix4x4 XyzScaling = Matrix4x4.Transpose(Matrix4x4.Identity); + + /// + /// Bradford chromatic adaptation transform matrix (used in CMCCAT97) + /// + public static readonly Matrix4x4 Bradford + = Matrix4x4.Transpose(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 + }); + + /// + /// Spectral sharpening and the Bradford transform + /// + public static readonly Matrix4x4 BradfordSharp + = Matrix4x4.Transpose(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 + }); + + /// + /// CMCCAT2000 (fitted from all available color data sets) + /// + public static readonly Matrix4x4 CMCCAT2000 + = Matrix4x4.Transpose(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 + }); + + /// + /// CAT02 (optimized for minimizing CIELAB differences) + /// + public static readonly Matrix4x4 CAT02 + = Matrix4x4.Transpose(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 + }); +} diff --git a/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs new file mode 100644 index 000000000..0505395de --- /dev/null +++ b/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs @@ -0,0 +1,90 @@ +// 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; + +/// +/// Implementation of the Von Kries chromatic adaptation model. +/// +/// +/// Transformation described here: +/// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html +/// +public static class VonKriesChromaticAdaptation +{ + /// + /// Performs a linear transformation of a source color in to the destination color. + /// + /// Doesn't crop the resulting color space coordinates (e.g. allows negative values for XYZ coordinates). + /// The color profile conversion options. + /// The source color. + /// The + public static CieXyz Transform(ColorConversionOptions options, in CieXyz source) + { + CieXyz sourceWhitePoint = options.WhitePoint; + CieXyz destinationWhitePoint = options.TargetWhitePoint; + + if (sourceWhitePoint.Equals(destinationWhitePoint)) + { + return new(source.X, source.Y, source.Z); + } + + Matrix4x4 matrix = options.AdaptationMatrix; + + Vector3 sourceColorLms = Vector3.Transform(source.ToVector3(), matrix); + Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix); + Vector3 targetWhitePointLms = Vector3.Transform(destinationWhitePoint.ToVector3(), matrix); + + Vector3 vector = targetWhitePointLms / sourceWhitePointLms; + Vector3 targetColorLms = Vector3.Multiply(vector, sourceColorLms); + + Matrix4x4.Invert(matrix, out Matrix4x4 inverseMatrix); + return new CieXyz(Vector3.Transform(targetColorLms, inverseMatrix)); + } + + /// + /// Performs a bulk linear transformation of a source color in to the destination color. + /// + /// Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates). + /// The color profile conversion options. + /// The span to the source colors. + /// The span to the destination colors. + public static void Transform(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + int count = source.Length; + + CieXyz sourceWhitePoint = options.WhitePoint; + CieXyz destinationWhitePoint = options.TargetWhitePoint; + + if (sourceWhitePoint.Equals(destinationWhitePoint)) + { + source.CopyTo(destination[..count]); + return; + } + + Matrix4x4 matrix = options.AdaptationMatrix; + Matrix4x4.Invert(matrix, out Matrix4x4 inverseMatrix); + + ref CieXyz sourceBase = ref MemoryMarshal.GetReference(source); + ref CieXyz destinationBase = ref MemoryMarshal.GetReference(destination); + + for (nuint i = 0; i < (uint)count; i++) + { + ref CieXyz sp = ref Unsafe.Add(ref sourceBase, i); + ref CieXyz dp = ref Unsafe.Add(ref destinationBase, i); + + Vector3 sourceColorLms = Vector3.Transform(sp.ToVector3(), matrix); + Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix); + Vector3 targetWhitePointLms = Vector3.Transform(destinationWhitePoint.ToVector3(), matrix); + + Vector3 vector = targetWhitePointLms / sourceWhitePointLms; + Vector3 targetColorLms = Vector3.Multiply(vector, sourceColorLms); + dp = new CieXyz(Vector3.Transform(targetColorLms, inverseMatrix)); + } + } +} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs index 97e9cee81..8f33e59f5 100644 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs +++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs @@ -8,7 +8,7 @@ using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorSpaces.Conversion; /// -/// Implementation of the von Kries chromatic adaptation model. +/// Implementation of the Von Kries chromatic adaptation model. /// /// /// Transformation described here: diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs new file mode 100644 index 000000000..74fa13216 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Allows the approximate comparison of color profile component values. +/// +internal readonly struct ApproximateColorProfileComparer : + IEqualityComparer, + IEqualityComparer, + IEqualityComparer +{ + private readonly float epsilon; + + /// + /// Initializes a new instance of the struct. + /// + /// The comparison error difference epsilon to use. + public ApproximateColorProfileComparer(float epsilon = 1f) => this.epsilon = epsilon; + + public bool Equals(CieLab x, CieLab y) => this.Equals(x.L, y.L) && this.Equals(x.A, y.A) && this.Equals(x.B, y.B); + + public bool Equals(CieXyz x, CieXyz y) => this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y) && this.Equals(x.Z, y.Z); + + public bool Equals(Lms x, Lms y) => this.Equals(x.L, y.L) && this.Equals(x.M, y.M) && this.Equals(x.S, y.S); + + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] Lms obj) => obj.GetHashCode(); + + private bool Equals(float x, float y) + { + float d = x - y; + return d >= -this.epsilon && d <= this.epsilon; + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs new file mode 100644 index 000000000..da2bcbc87 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs @@ -0,0 +1,86 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieXyzAndCieLabConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001f); + + [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(l, a, b); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + CieXyz expected = new(x, y, z); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [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(x, y, z); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + CieLab expected = new(l, a, b); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs new file mode 100644 index 000000000..60938991e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs @@ -0,0 +1,81 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using original colorful library. +/// +public class CieXyzAndLmsConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001f); + + [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(l, m, s); + ColorProfileConverter converter = new(); + CieXyz expected = new(x, y, z); + + Span inputSpan = new Lms[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [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(x, y, z); + ColorProfileConverter converter = new(); + Lms expected = new(l, m, s); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Lms[5]; + + // Act + Lms actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index a389c8ab8..41e6e525f 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -39,7 +39,7 @@ Do not update or consolidate BenchmarkDotNet. https://github.com/dotnet/arcade/issues/8483 --> - + From 8bc794af659d0d00ee039482dfbe0ca25f8f661f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 11 Mar 2024 21:50:46 +1000 Subject: [PATCH 02/79] Add CieLch --- src/ImageSharp/ColorProfiles/CieLab.cs | 4 +- src/ImageSharp/ColorProfiles/CieLch.cs | 166 ++++++++++++++++++ ...rProfileConverterExtensionsCieXyzCieLab.cs | 52 ++++++ .../ApproximateColorProfileComparer.cs | 7 +- .../CieLabAndCieLchConversionTests.cs | 88 ++++++++++ 5 files changed, 313 insertions(+), 4 deletions(-) create mode 100644 src/ImageSharp/ColorProfiles/CieLch.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs index c06fe765c..248559f73 100644 --- a/src/ImageSharp/ColorProfiles/CieLab.cs +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -155,9 +155,7 @@ public readonly struct CieLab : IProfileConnectingSpace CieXyz whitePoint = options.WhitePoint; Vector3 wxyz = new(whitePoint.X, whitePoint.Y, whitePoint.Z); - - // Avoids XYZ coordinates out range (restricted by 0 and XYZ reference white) - Vector3 xyzr = Vector3.Clamp(new Vector3(xr, yr, zr), Vector3.Zero, Vector3.One); + Vector3 xyzr = new(xr, yr, zr); return new(xyzr * wxyz); } diff --git a/src/ImageSharp/ColorProfiles/CieLch.cs b/src/ImageSharp/ColorProfiles/CieLch.cs new file mode 100644 index 000000000..4b48e1c8d --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieLch.cs @@ -0,0 +1,166 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color. +/// +/// +public readonly struct CieLch : IColorProfile +{ + /// + /// D50 standard illuminant. + /// Used when reference white is not specified explicitly. + /// + public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; + + private static readonly Vector3 Min = new(0, -200, 0); + private static readonly Vector3 Max = new(100, 200, 360); + + /// + /// Initializes a new instance of the struct. + /// + /// The lightness dimension. + /// The chroma, relative saturation. + /// The hue in degrees. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLch(float l, float c, float h) + : this(new Vector3(l, c, h)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l, c, h components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLch(Vector3 vector) + { + vector = Vector3.Clamp(vector, Min, Max); + this.L = vector.X; + this.C = vector.Y; + this.H = vector.Z; + } + + /// + /// Gets the lightness dimension. + /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). + /// + public readonly float L { get; } + + /// + /// Gets the a chroma component. + /// A value ranging from 0 to 200. + /// + public readonly float C { get; } + + /// + /// Gets the h° hue component in degrees. + /// A value ranging from 0 to 360. + /// + public readonly float H { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(CieLch left, CieLch right) => left.Equals(right); + + /// + /// Compares two objects for inequality + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(CieLch left, CieLch right) => !left.Equals(right); + + /// + public override int GetHashCode() + => HashCode.Combine(this.L, this.C, this.H); + + /// + public override string ToString() => FormattableString.Invariant($"CieLch({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object? obj) => obj is CieLch other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieLch other) + => this.L.Equals(other.L) + && this.C.Equals(other.C) + && this.H.Equals(other.H); + + /// + public static CieLch FromProfileConnectingSpace(ColorConversionOptions options, in CieLab source) + { + // Conversion algorithm described here: + // https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC + float l = source.L, a = source.A, b = source.B; + float c = MathF.Sqrt((a * a) + (b * b)); + float hRadians = MathF.Atan2(b, a); + float hDegrees = GeometryUtilities.RadianToDegree(hRadians); + + // Wrap the angle round at 360. + hDegrees %= 360; + + // Make sure it's not negative. + while (hDegrees < 0) + { + hDegrees += 360; + } + + return new CieLch(l, c, hDegrees); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + for (int i = 0; i < source.Length; i++) + { + CieLab lab = source[i]; + destination[i] = FromProfileConnectingSpace(options, in lab); + } + } + + /// + public CieLab ToProfileConnectingSpace(ColorConversionOptions options) + { + // Conversion algorithm described here: + // https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC + float l = this.L, c = this.C, hDegrees = this.H; + float hRadians = GeometryUtilities.DegreeToRadian(hDegrees); + + float a = c * MathF.Cos(hRadians); + float b = c * MathF.Sin(hRadians); + + return new CieLab(l, a, b); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + for (int i = 0; i < source.Length; i++) + { + CieLch lch = source[i]; + destination[i] = lch.ToProfileConnectingSpace(options); + } + } +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs new file mode 100644 index 000000000..949cd6c8e --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsCieXyzCieLab +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + CieXyz pcsFrom = source.ToProfileConnectingSpace(options); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + + // Convert between PCS + CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFrom); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFrom = pcsFromOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFrom); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + + // Convert between PCS. + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsTo = pcsToOwner.GetSpan(); + CieLab.FromProfileConnectionSpace(options, pcsFrom, pcsTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsTo, destination); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs index 74fa13216..5c2f9afbd 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -12,7 +12,8 @@ namespace SixLabors.ImageSharp.Tests.ColorProfiles; internal readonly struct ApproximateColorProfileComparer : IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer { private readonly float epsilon; @@ -28,12 +29,16 @@ internal readonly struct ApproximateColorProfileComparer : public bool Equals(Lms x, Lms y) => this.Equals(x.L, y.L) && this.Equals(x.M, y.M) && this.Equals(x.S, y.S); + public bool Equals(CieLch x, CieLch y) => this.Equals(x.L, y.L) && this.Equals(x.C, y.C) && this.Equals(x.H, y.H); + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] Lms obj) => obj.GetHashCode(); + public int GetHashCode([DisallowNull] CieLch obj) => obj.GetHashCode(); + private bool Equals(float x, float y) { float d = x - y; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs new file mode 100644 index 000000000..0eaf30b81 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs @@ -0,0 +1,88 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieLabAndCieLchConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(54.2917, 106.8391, 40.8526, 54.2917, 80.8125, 69.8851)] + [InlineData(100, 0, 0, 100, 0, 0)] + [InlineData(100, 50, 180, 100, -50, 0)] + [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] + [InlineData(10, 36.0555, 123.6901, 10, -20, 30)] + [InlineData(10, 36.0555, 303.6901, 10, 20, -30)] + [InlineData(10, 36.0555, 236.3099, 10, -20, -30)] + public void Convert_Lch_to_Lab(float l, float c, float h, float l2, float a, float b) + { + // Arrange + CieLch input = new(l, c, h); + CieLab expected = new(l2, a, b); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D50 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(54.2917, 80.8125, 69.8851, 54.2917, 106.8391, 40.8526)] + [InlineData(100, 0, 0, 100, 0, 0)] + [InlineData(100, -50, 0, 100, 50, 180)] + [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] + [InlineData(10, -20, 30, 10, 36.0555, 123.6901)] + [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] + [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] + public void Convert_Lab_to_Lch(float l, float a, float b, float l2, float c, float h) + { + // Arrange + CieLab input = new(l, a, b); + CieLch expected = new(l2, c, h); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D50 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + CieLch actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 3703cb17af42798f9135393f5c7000bf7b825123 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 20 Mar 2024 00:00:08 +1000 Subject: [PATCH 03/79] Wire up Rgb working spaces --- .../ChromaticAdaptionWhitePointSource.cs | 20 ++ src/ImageSharp/ColorProfiles/CieLab.cs | 3 + src/ImageSharp/ColorProfiles/CieLch.cs | 3 + .../CieXyChromaticityCoordinates.cs | 83 ++++++ src/ImageSharp/ColorProfiles/CieXyz.cs | 3 + .../ColorProfiles/ColorConversionOptions.cs | 12 + ...rProfileConverterExtensionsCieLabCieXyz.cs | 4 +- ...rProfileConverterExtensionsCieXyzCieLab.cs | 6 +- ...rProfileConverterExtensionsCieXyzCieXyz.cs | 4 +- .../Companding/CompandingUtilities.cs | 171 ++++++++++++ .../Companding/GammaCompanding.cs | 56 ++++ .../ColorProfiles/Companding/LCompanding.cs | 71 +++++ .../Companding/Rec2020Companding.cs | 75 ++++++ .../Companding/Rec709Companding.cs | 71 +++++ .../Companding/SRgbCompanding.cs | 71 +++++ ...elf,TProfileSpace}.cs => IColorProfile.cs} | 46 ++-- src/ImageSharp/ColorProfiles/Lms.cs | 3 + src/ImageSharp/ColorProfiles/Rgb.cs | 246 ++++++++++++++++++ .../RgbPrimariesChromaticityCoordinates.cs | 82 ++++++ .../ColorProfiles/RgbWorkingSpaces.cs | 114 ++++++++ .../VonKriesChromaticAdaptation.cs | 44 +++- .../WorkingSpaces/GammaWorkingSpace.cs | 68 +++++ .../WorkingSpaces/LWorkingSpace.cs | 35 +++ .../WorkingSpaces/Rec2020WorkingSpace.cs | 35 +++ .../WorkingSpaces/Rec709WorkingSpace.cs | 35 +++ .../WorkingSpaces/RgbWorkingSpace.cs | 85 ++++++ .../WorkingSpaces/SRgbWorkingSpace.cs | 35 +++ 27 files changed, 1445 insertions(+), 36 deletions(-) create mode 100644 src/ImageSharp/ColorProfiles/ChromaticAdaptionWhitePointSource.cs create mode 100644 src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs create mode 100644 src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs create mode 100644 src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs create mode 100644 src/ImageSharp/ColorProfiles/Companding/LCompanding.cs create mode 100644 src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs create mode 100644 src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs create mode 100644 src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs rename src/ImageSharp/ColorProfiles/{IColorProfile{TSelf,TProfileSpace}.cs => IColorProfile.cs} (82%) create mode 100644 src/ImageSharp/ColorProfiles/Rgb.cs create mode 100644 src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs create mode 100644 src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs create mode 100644 src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs create mode 100644 src/ImageSharp/ColorProfiles/WorkingSpaces/LWorkingSpace.cs create mode 100644 src/ImageSharp/ColorProfiles/WorkingSpaces/Rec2020WorkingSpace.cs create mode 100644 src/ImageSharp/ColorProfiles/WorkingSpaces/Rec709WorkingSpace.cs create mode 100644 src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs create mode 100644 src/ImageSharp/ColorProfiles/WorkingSpaces/SRgbWorkingSpace.cs diff --git a/src/ImageSharp/ColorProfiles/ChromaticAdaptionWhitePointSource.cs b/src/ImageSharp/ColorProfiles/ChromaticAdaptionWhitePointSource.cs new file mode 100644 index 000000000..7e4a9c413 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ChromaticAdaptionWhitePointSource.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Enumerate the possible sources of the white point used in chromatic adaptation. +/// +public enum ChromaticAdaptionWhitePointSource +{ + /// + /// The white point of the source color space. + /// + WhitePoint, + + /// + /// The white point of the source working space. + /// + RgbWorkingSpace +} diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs index 248559f73..3578e2713 100644 --- a/src/ImageSharp/ColorProfiles/CieLab.cs +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -172,4 +172,7 @@ public readonly struct CieLab : IProfileConnectingSpace destination[i] = lab.ToProfileConnectingSpace(options); } } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; } diff --git a/src/ImageSharp/ColorProfiles/CieLch.cs b/src/ImageSharp/ColorProfiles/CieLch.cs index 4b48e1c8d..23f250d0c 100644 --- a/src/ImageSharp/ColorProfiles/CieLch.cs +++ b/src/ImageSharp/ColorProfiles/CieLch.cs @@ -163,4 +163,7 @@ public readonly struct CieLch : IColorProfile destination[i] = lch.ToProfileConnectingSpace(options); } } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; } diff --git a/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs new file mode 100644 index 000000000..b55abdd05 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs @@ -0,0 +1,83 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; + +// ReSharper disable CompareOfFloatsByEqualityOperator +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents the coordinates of CIEXY chromaticity space. +/// +public readonly struct CieXyChromaticityCoordinates : IEquatable +{ + /// + /// Initializes a new instance of the struct. + /// + /// Chromaticity coordinate x (usually from 0 to 1) + /// Chromaticity coordinate y (usually from 0 to 1) + [MethodImpl(InliningOptions.ShortMethod)] + public CieXyChromaticityCoordinates(float x, float y) + { + this.X = x; + this.Y = y; + } + + /// + /// Gets the chromaticity X-coordinate. + /// + /// + /// Ranges usually from 0 to 1. + /// + public readonly float X { get; } + + /// + /// Gets the chromaticity Y-coordinate + /// + /// + /// Ranges usually from 0 to 1. + /// + public readonly float Y { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) + => left.Equals(right); + + /// + /// Compares two objects for inequality + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) + => !left.Equals(right); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() + => HashCode.Combine(this.X, this.Y); + + /// + public override string ToString() + => FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})"); + + /// + public override bool Equals(object? obj) + => obj is CieXyChromaticityCoordinates other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(CieXyChromaticityCoordinates other) + => this.X.Equals(other.X) && this.Y.Equals(other.Y); +} diff --git a/src/ImageSharp/ColorProfiles/CieXyz.cs b/src/ImageSharp/ColorProfiles/CieXyz.cs index b22ab16f9..ec28a232d 100644 --- a/src/ImageSharp/ColorProfiles/CieXyz.cs +++ b/src/ImageSharp/ColorProfiles/CieXyz.cs @@ -121,4 +121,7 @@ public readonly struct CieXyz : IProfileConnectingSpace Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); source.CopyTo(destination[..source.Length]); } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; } diff --git a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs index de32aa54d..384ab6a7f 100644 --- a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs +++ b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs @@ -2,6 +2,8 @@ // Licensed under the Six Labors Split License. using System.Numerics; +using SixLabors.ColorProfiles; +using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.ColorProfiles; @@ -33,6 +35,16 @@ public class ColorConversionOptions /// public CieXyz TargetWhitePoint { get; init; } = Illuminants.D50; + /// + /// Gets the source working space used for companding in conversions from/to XYZ color space. + /// + public RgbWorkingSpace RgbWorkingSpace { get; init; } = RgbWorkingSpaces.SRgb; + + /// + /// Gets the destination working space used for companding in conversions from/to XYZ color space. + /// + public RgbWorkingSpace TargetRgbWorkingSpace { get; init; } = RgbWorkingSpaces.SRgb; + /// /// Gets the transformation matrix used in conversion to perform chromatic adaptation. /// diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs index 481280b85..8860777cd 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs @@ -21,7 +21,7 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz CieXyz pcsTo = pcsFrom.ToProfileConnectingSpace(options); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, in pcsTo); + VonKriesChromaticAdaptation.Transform(options, in pcsTo); // Convert to output from PCS return TTo.FromProfileConnectingSpace(options, pcsTo); @@ -44,7 +44,7 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz CieLab.ToProfileConnectionSpace(options, pcsFrom, pcsTo); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsTo, pcsTo); + VonKriesChromaticAdaptation.Transform(options, pcsTo, pcsTo); // Convert to output from PCS TTo.FromProfileConnectionSpace(options, pcsTo, destination); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs index 949cd6c8e..50317311c 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Buffers; @@ -18,7 +18,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab CieXyz pcsFrom = source.ToProfileConnectingSpace(options); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + VonKriesChromaticAdaptation.Transform(options, in pcsFrom); // Convert between PCS CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFrom); @@ -39,7 +39,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab TFrom.ToProfileConnectionSpace(options, source, pcsFrom); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); // Convert between PCS. using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs index ebe07003e..2380bb15e 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs @@ -18,7 +18,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz CieXyz pcsFrom = source.ToProfileConnectingSpace(options); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + VonKriesChromaticAdaptation.Transform(options, in pcsFrom); // Convert between PCS CieXyz pcsTo = CieXyz.FromProfileConnectingSpace(options, in pcsFrom); @@ -39,7 +39,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz TFrom.ToProfileConnectionSpace(options, source, pcsFrom); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); // Convert between PCS. using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); diff --git a/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs b/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs new file mode 100644 index 000000000..44dae35a9 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs @@ -0,0 +1,171 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Collections.Concurrent; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace SixLabors.ImageSharp.ColorProfiles.Companding; + +/// +/// Companding utilities that allow the accelerated compression-expansion of color channels. +/// +public static class CompandingUtilities +{ + private const int Length = Scale + 2; // 256kb @ 16bit precision. + private const int Scale = (1 << 16) - 1; + private static readonly ConcurrentDictionary<(Type, double), Lazy> LookupTables = new(); + + /// + /// Lazily creates and stores a companding lookup table using the given function and modifier. + /// + /// The type of companding function. + /// The companding function. + /// A modifier to pass to the function. + /// The array. + public static Lazy GetLookupTable(Func compandingFunction, double modifier = 0) + => LookupTables.GetOrAdd((typeof(T), modifier), args => new(() => CreateLookupTableImpl(compandingFunction, args.Item2), true)); + + /// + /// Creates a companding lookup table using the given function. + /// + /// The companding function. + /// A modifier to pass to the function. + /// The array. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float[] CreateLookupTableImpl(Func compandingFunction, double modifier = 0) + { + float[] result = new float[Length]; + + for (int i = 0; i < result.Length; i++) + { + double d = (double)i / Scale; + d = compandingFunction(d, modifier); + result[i] = (float)d; + } + + return result; + } + + /// + /// Performs the companding operation on the given vectors using the given table. + /// + /// The span of vectors. + /// The lookup table. + public static void Compand(Span vectors, float[] table) + { + DebugGuard.MustBeGreaterThanOrEqualTo(table.Length, Length, nameof(table)); + + if (Avx2.IsSupported && vectors.Length >= 2) + { + CompandAvx2(vectors, table); + + if (Numerics.Modulo2(vectors.Length) != 0) + { + // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. + ref Vector4 last = ref MemoryMarshal.GetReference(vectors[^1..]); + last = Compand(last, table); + } + } + else + { + CompandScalar(vectors, table); + } + } + + /// + /// Performs the companding operation on the given vector using the given table. + /// + /// The vector. + /// The lookup table. + /// The + public static Vector4 Compand(Vector4 vector, float[] table) + { + DebugGuard.MustBeGreaterThanOrEqualTo(table.Length, Length, nameof(table)); + + Vector4 zero = Vector4.Zero; + Vector4 scale = new(Scale); + + Vector4 multiplied = Numerics.Clamp(vector * Scale, zero, scale); + + float f0 = multiplied.X; + float f1 = multiplied.Y; + float f2 = multiplied.Z; + + uint i0 = (uint)f0; + uint i1 = (uint)f1; + uint i2 = (uint)f2; + + // Alpha is already a linear representation of opacity so we do not want to convert it. + vector.X = Numerics.Lerp(table[i0], table[i0 + 1], f0 - (int)i0); + vector.Y = Numerics.Lerp(table[i1], table[i1 + 1], f1 - (int)i1); + vector.Z = Numerics.Lerp(table[i2], table[i2 + 1], f2 - (int)i2); + + return vector; + } + + private static unsafe void CompandAvx2(Span vectors, float[] table) + { + fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table)) + { + Vector256 scale = Vector256.Create((float)Scale); + Vector256 zero = Vector256.Zero; + Vector256 offset = Vector256.Create(1); + + // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 + ref Vector256 vectorsBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors)); + ref Vector256 vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length / 2u); + + while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) + { + Vector256 multiplied = Avx.Multiply(scale, vectorsBase); + multiplied = Avx.Min(Avx.Max(zero, multiplied), scale); + + Vector256 truncated = Avx.ConvertToVector256Int32WithTruncation(multiplied); + Vector256 truncatedF = Avx.ConvertToVector256Single(truncated); + + Vector256 low = Avx2.GatherVector256(tablePointer, truncated, sizeof(float)); + Vector256 high = Avx2.GatherVector256(tablePointer, Avx2.Add(truncated, offset), sizeof(float)); + + // Alpha is already a linear representation of opacity so we do not want to convert it. + Vector256 companded = Numerics.Lerp(low, high, Avx.Subtract(multiplied, truncatedF)); + vectorsBase = Avx.Blend(companded, vectorsBase, Numerics.BlendAlphaControl); + vectorsBase = ref Unsafe.Add(ref vectorsBase, 1); + } + } + } + + private static unsafe void CompandScalar(Span vectors, float[] table) + { + fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table)) + { + Vector4 zero = Vector4.Zero; + Vector4 scale = new(Scale); + ref Vector4 vectorsBase = ref MemoryMarshal.GetReference(vectors); + ref Vector4 vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length); + + while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) + { + Vector4 multiplied = Numerics.Clamp(vectorsBase * Scale, zero, scale); + + float f0 = multiplied.X; + float f1 = multiplied.Y; + float f2 = multiplied.Z; + + uint i0 = (uint)f0; + uint i1 = (uint)f1; + uint i2 = (uint)f2; + + // Alpha is already a linear representation of opacity so we do not want to convert it. + vectorsBase.X = Numerics.Lerp(tablePointer[i0], tablePointer[i0 + 1], f0 - (int)i0); + vectorsBase.Y = Numerics.Lerp(tablePointer[i1], tablePointer[i1 + 1], f1 - (int)i1); + vectorsBase.Z = Numerics.Lerp(tablePointer[i2], tablePointer[i2 + 1], f2 - (int)i2); + + vectorsBase = ref Unsafe.Add(ref vectorsBase, 1); + } + } + } +} diff --git a/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs new file mode 100644 index 000000000..28e9a4641 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs @@ -0,0 +1,56 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.ColorProfiles.Companding; + +/// +/// Implements gamma companding. +/// +/// +/// +/// +/// +public static class GammaCompanding +{ + private static Func CompressFunction => (d, m) => Math.Pow(d, 1 / m); + + private static Func ExpandFunction => Math.Pow; + + /// + /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy. + /// + /// The span of vectors. + /// The gamma value. + public static void Compress(Span vectors, double gamma) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction, gamma).Value); + + /// + /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. + /// + /// The span of vectors. + /// The gamma value. + public static void Expand(Span vectors, double gamma) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction, gamma).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The gamma value. + /// The . + public static Vector4 Compress(Vector4 vector, double gamma) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction, gamma).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The gamma value. + /// The . + public static Vector4 Expand(Vector4 vector, double gamma) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction, gamma).Value); + + private class GammaCompandingKey; +} diff --git a/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs new file mode 100644 index 000000000..79349d1c5 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.ColorProfiles.Companding; + +/// +/// Implements L* companding. +/// +/// +/// For more info see: +/// +/// +/// +public static class LCompanding +{ + private static Func CompressFunction + => (d, _) => + { + if (d <= CieConstants.Epsilon) + { + return (d * CieConstants.Kappa) / 100; + } + + return (1.16 * Math.Pow(d, 0.3333333)) - 0.16; + }; + + private static Func ExpandFunction + => (d, _) => + { + if (d <= 0.08) + { + return (100 * d) / CieConstants.Kappa; + } + + return Numerics.Pow3(((float)(d + 0.16f)) / 1.16f); + }; + + /// + /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Compress(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Expand(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Compress(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Expand(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + private class LCompandingKey; +} diff --git a/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs b/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs new file mode 100644 index 000000000..9beab3a86 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs @@ -0,0 +1,75 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.ColorProfiles.Companding; + +/// +/// Implements Rec. 2020 companding function. +/// +/// +/// +/// +public static class Rec2020Companding +{ + private const double Alpha = 1.09929682680944; + private const double AlphaMinusOne = Alpha - 1; + private const double Beta = 0.018053968510807; + private const double InverseBeta = Beta * 4.5; + private const double Epsilon = 1 / 0.45; + + private static Func CompressFunction + => (d, _) => + { + if (d < Beta) + { + return 4.5 * d; + } + + return (Alpha * Math.Pow(d, 0.45)) - AlphaMinusOne; + }; + + private static Func ExpandFunction + => (d, _) => + { + if (d < InverseBeta) + { + return d / 4.5; + } + + return Math.Pow((d + AlphaMinusOne) / Alpha, Epsilon); + }; + + /// + /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Compress(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Expand(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Compress(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Expand(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + private class Rec2020CompandingKey; +} diff --git a/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs b/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs new file mode 100644 index 000000000..93a32672c --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.ColorProfiles.Companding; + +/// +/// Implements the Rec. 709 companding function. +/// +/// +/// http://en.wikipedia.org/wiki/Rec._709 +/// +public static class Rec709Companding +{ + private const double Epsilon = 1 / 0.45; + + private static Func CompressFunction + => (d, _) => + { + if (d < 0.018) + { + return 4.5 * d; + } + + return (1.099 * Math.Pow(d, 0.45)) - 0.099; + }; + + private static Func ExpandFunction + => (d, _) => + { + if (d < 0.081) + { + return d / 4.5; + } + + return Math.Pow((d + 0.099) / 1.099, Epsilon); + }; + + /// + /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Compress(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Expand(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Compress(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Expand(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + private class Rec2020CompandingKey; +} diff --git a/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs new file mode 100644 index 000000000..04e4007a8 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.ColorProfiles.Companding; + +/// +/// Implements sRGB companding. +/// +/// +/// For more info see: +/// +/// +/// +public static class SRgbCompanding +{ + private static Func CompressFunction + => (d, _) => + { + if (d <= (0.04045 / 12.92)) + { + return d * 12.92; + } + + return (1.055 * Math.Pow(d, 1.0 / 2.4)) - 0.055; + }; + + private static Func ExpandFunction + => (d, _) => + { + if (d <= 0.04045) + { + return d / 12.92; + } + + return Math.Pow((d + 0.055) / 1.055, 2.4); + }; + + /// + /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Compress(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. + /// + /// The span of vectors. + public static void Expand(Span vectors) + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Compress(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public static Vector4 Expand(Vector4 vector) + => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + + private class SRgbCompandingKey; +} diff --git a/src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs b/src/ImageSharp/ColorProfiles/IColorProfile.cs similarity index 82% rename from src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs rename to src/ImageSharp/ColorProfiles/IColorProfile.cs index e1d4cae35..ac4b425fe 100644 --- a/src/ImageSharp/ColorProfiles/IColorProfile{TSelf,TProfileSpace}.cs +++ b/src/ImageSharp/ColorProfiles/IColorProfile.cs @@ -1,24 +1,29 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.ColorProfiles; +/// +/// Defines the contract for all color profiles. +/// +public interface IColorProfile +{ + /// + /// Gets the chromatic adaption white point source. + /// + /// The . + public static abstract ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource(); +} + /// /// Defines the contract for all color profiles. /// /// The type of color profile. /// The type of color profile connecting space. -public interface IColorProfile : IEquatable +public interface IColorProfile : IColorProfile, IEquatable where TSelf : IColorProfile where TProfileSpace : struct, IProfileConnectingSpace { - /// - /// Converts the color to the profile connection space. - /// - /// The color profile conversion options. - /// The . - public TProfileSpace ToProfileConnectingSpace(ColorConversionOptions options); - #pragma warning disable CA1000 // Do not declare static members on generic types /// /// Converts the color from the profile connection space. @@ -28,14 +33,6 @@ public interface IColorProfile : IEquatable /// The . public static abstract TSelf FromProfileConnectingSpace(ColorConversionOptions options, in TProfileSpace source); - /// - /// Converts the span of colors to the profile connection space. - /// - /// The color profile conversion options. - /// The color span to convert from. - /// The color profile span to write the results to. - public static abstract void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination); - /// /// Converts the span of colors from the profile connection space. /// @@ -43,5 +40,20 @@ public interface IColorProfile : IEquatable /// The color profile span to convert from. /// The color span to write the results to. public static abstract void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination); + + /// + /// Converts the color to the profile connection space. + /// + /// The color profile conversion options. + /// The . + public TProfileSpace ToProfileConnectingSpace(ColorConversionOptions options); + + /// + /// Converts the span of colors to the profile connection space. + /// + /// The color profile conversion options. + /// The color span to convert from. + /// The color profile span to write the results to. + public static abstract void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination); #pragma warning restore CA1000 // Do not declare static members on generic types } diff --git a/src/ImageSharp/ColorProfiles/Lms.cs b/src/ImageSharp/ColorProfiles/Lms.cs index e11c1ca87..cb060185d 100644 --- a/src/ImageSharp/ColorProfiles/Lms.cs +++ b/src/ImageSharp/ColorProfiles/Lms.cs @@ -133,4 +133,7 @@ internal readonly struct Lms : IColorProfile destination[i] = lms.ToProfileConnectingSpace(options); } } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; } diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs new file mode 100644 index 000000000..2669d2c96 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Rgb.cs @@ -0,0 +1,246 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal readonly struct Rgb : IProfileConnectingSpace +{ + private static readonly Vector3 Min = Vector3.Zero; + private static readonly Vector3 Max = Vector3.One; + + /// + /// Initializes a new instance of the struct. + /// + /// The red component ranging between 0 and 1. + /// The green component ranging between 0 and 1. + /// The blue component ranging between 0 and 1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Rgb(float r, float g, float b) + : this(new Vector3(r, g, b)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the r, g, b components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Rgb(Vector3 source) + { + source = Vector3.Clamp(source, Min, Max); + this.R = source.X; + this.G = source.Y; + this.B = source.Z; + } + + /// + /// Gets the red component. + /// A value usually ranging between 0 and 1. + /// + public readonly float R { get; } + + /// + /// Gets the green component. + /// A value usually ranging between 0 and 1. + /// + public readonly float G { get; } + + /// + /// Gets the blue component. + /// A value usually ranging between 0 and 1. + /// + public readonly float B { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Rgb left, Rgb right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Rgb left, Rgb right) => !left.Equals(right); + + /// + public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); + + /// + public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is Rgb other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Rgb other) + => this.R.Equals(other.R) + && this.G.Equals(other.G) + && this.B.Equals(other.B); + + /// + public static Rgb FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + { + // Convert to linear rgb then compress. + Rgb linear = new(Vector3.Transform(source.ToVector3(), GetCieXyzToRgbMatrix(options.TargetRgbWorkingSpace))); + return FromScaledVector4(options.TargetRgbWorkingSpace.Compress(linear.ToScaledVector4())); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + Matrix4x4 matrix = GetCieXyzToRgbMatrix(options.TargetRgbWorkingSpace); + for (int i = 0; i < source.Length; i++) + { + // Convert to linear rgb then compress. + Rgb linear = new(Vector3.Transform(source[i].ToVector3(), matrix)); + Vector4 nonlinear = options.TargetRgbWorkingSpace.Compress(linear.ToScaledVector4()); + destination[i] = FromScaledVector4(nonlinear); + } + } + + /// + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + { + // First expand to linear rgb + Rgb linear = FromScaledVector4(options.RgbWorkingSpace.Expand(this.ToScaledVector4())); + + // Then convert to xyz + return new CieXyz(Vector3.Transform(linear.ToScaledVector3(), GetRgbToCieXyzMatrix(options.RgbWorkingSpace))); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + Matrix4x4 matrix = GetRgbToCieXyzMatrix(options.RgbWorkingSpace); + for (int i = 0; i < source.Length; i++) + { + Rgb rgb = source[i]; + + // First expand to linear rgb + Rgb linear = FromScaledVector4(options.RgbWorkingSpace.Expand(rgb.ToScaledVector4())); + + // Then convert to xyz + destination[i] = new CieXyz(Vector3.Transform(linear.ToScaledVector3(), matrix)); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; + + /// + /// Initializes the pixel instance from a generic scaled . + /// + /// The vector to load the pixel from. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb FromScaledVector3(Vector3 source) => new(source); + + /// + /// Initializes the pixel instance from a generic scaled . + /// + /// The vector to load the pixel from. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb FromScaledVector4(Vector4 source) => new(source.X, source.Y, source.Z); + + /// + /// Expands the color into a generic ("scaled") representation + /// with values scaled and clamped between 0 and 1. + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 ToScaledVector3() => new(this.R, this.G, this.B); + + /// + /// Expands the color into a generic ("scaled") representation + /// with values scaled and clamped between 0 and 1. + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector4 ToScaledVector4() => new(this.R, this.G, this.B, 1f); + + private static Matrix4x4 GetCieXyzToRgbMatrix(RgbWorkingSpace workingSpace) + { + Matrix4x4 matrix = GetRgbToCieXyzMatrix(workingSpace); + Matrix4x4.Invert(matrix, out Matrix4x4 inverseMatrix); + return inverseMatrix; + } + + private static Matrix4x4 GetRgbToCieXyzMatrix(RgbWorkingSpace workingSpace) + { + DebugGuard.NotNull(workingSpace, nameof(workingSpace)); + RgbPrimariesChromaticityCoordinates chromaticity = workingSpace.ChromaticityCoordinates; + + float xr = chromaticity.R.X; + float xg = chromaticity.G.X; + float xb = chromaticity.B.X; + float yr = chromaticity.R.Y; + float yg = chromaticity.G.Y; + float yb = chromaticity.B.Y; + + float mXr = xr / yr; + float mZr = (1 - xr - yr) / yr; + + float mXg = xg / yg; + float mZg = (1 - xg - yg) / yg; + + float mXb = xb / yb; + float mZb = (1 - xb - yb) / yb; + + Matrix4x4 xyzMatrix = new() + { + M11 = mXr, + M21 = mXg, + M31 = mXb, + M12 = 1F, + M22 = 1F, + M32 = 1F, + M13 = mZr, + M23 = mZg, + M33 = mZb, + M44 = 1F + }; + + Matrix4x4.Invert(xyzMatrix, out Matrix4x4 inverseXyzMatrix); + + Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.ToVector3(), inverseXyzMatrix); + + // Use transposed Rows/Columns + // TODO: Is there a built in method for this multiplication? + return new Matrix4x4 + { + M11 = vector.X * mXr, + M21 = vector.Y * mXg, + M31 = vector.Z * mXb, + M12 = vector.X * 1, + M22 = vector.Y * 1, + M32 = vector.Z * 1, + M13 = vector.X * mZr, + M23 = vector.Y * mZg, + M33 = vector.Z * mZb, + M44 = 1F + }; + } +} diff --git a/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs new file mode 100644 index 000000000..90a5a57f9 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs @@ -0,0 +1,82 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents the chromaticity coordinates of RGB primaries. +/// One of the specifiers of . +/// +public readonly struct RgbPrimariesChromaticityCoordinates : IEquatable +{ + /// + /// Initializes a new instance of the struct. + /// + /// The chromaticity coordinates of the red channel. + /// The chromaticity coordinates of the green channel. + /// The chromaticity coordinates of the blue channel. + public RgbPrimariesChromaticityCoordinates(CieXyChromaticityCoordinates r, CieXyChromaticityCoordinates g, CieXyChromaticityCoordinates b) + { + this.R = r; + this.G = g; + this.B = b; + } + + /// + /// Gets the chromaticity coordinates of the red channel. + /// + public CieXyChromaticityCoordinates R { get; } + + /// + /// Gets the chromaticity coordinates of the green channel. + /// + public CieXyChromaticityCoordinates G { get; } + + /// + /// Gets the chromaticity coordinates of the blue channel. + /// + public CieXyChromaticityCoordinates B { get; } + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + public static bool operator ==(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right) + => left.Equals(right); + + /// + /// Compares two objects for inequality + /// + /// + /// The on the left side of the operand. + /// + /// + /// The on the right side of the operand. + /// + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + public static bool operator !=(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right) + => !left.Equals(right); + + /// + public override bool Equals(object? obj) + => obj is RgbPrimariesChromaticityCoordinates other && this.Equals(other); + + /// + public bool Equals(RgbPrimariesChromaticityCoordinates other) + => this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); + + /// + public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); +} diff --git a/src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs b/src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs new file mode 100644 index 000000000..c0d7f5074 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs @@ -0,0 +1,114 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; +using SixLabors.ImageSharp.ColorProfiles.Companding; +using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +namespace SixLabors.ColorProfiles; + +/// +/// Chromaticity coordinates based on: +/// +public static class RgbWorkingSpaces +{ + /// + /// sRgb working space. + /// + /// + /// Uses proper companding function, according to: + /// + /// + public static readonly RgbWorkingSpace SRgb = new SRgbWorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + + /// + /// Simplified sRgb working space (uses gamma companding instead of ). + /// See also . + /// + public static readonly RgbWorkingSpace SRgbSimplified = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + + /// + /// Rec. 709 (ITU-R Recommendation BT.709) working space. + /// + public static readonly RgbWorkingSpace Rec709 = new Rec709WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); + + /// + /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. + /// + public static readonly RgbWorkingSpace Rec2020 = new Rec2020WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); + + /// + /// ECI Rgb v2 working space. + /// + public static readonly RgbWorkingSpace ECIRgbv2 = new LWorkingSpace(Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + + /// + /// Adobe Rgb (1998) working space. + /// + public static readonly RgbWorkingSpace AdobeRgb1998 = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + + /// + /// Apple sRgb working space. + /// + public static readonly RgbWorkingSpace ApplesRgb = new GammaWorkingSpace(1.8F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + + /// + /// Best Rgb working space. + /// + public static readonly RgbWorkingSpace BestRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + + /// + /// Beta Rgb working space. + /// + public static readonly RgbWorkingSpace BetaRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); + + /// + /// Bruce Rgb working space. + /// + public static readonly RgbWorkingSpace BruceRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + + /// + /// CIE Rgb working space. + /// + public static readonly RgbWorkingSpace CIERgb = new GammaWorkingSpace(2.2F, Illuminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); + + /// + /// ColorMatch Rgb working space. + /// + public static readonly RgbWorkingSpace ColorMatchRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); + + /// + /// Don Rgb 4 working space. + /// + public static readonly RgbWorkingSpace DonRgb4 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + + /// + /// Ekta Space PS5 working space. + /// + public static readonly RgbWorkingSpace EktaSpacePS5 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); + + /// + /// NTSC Rgb working space. + /// + public static readonly RgbWorkingSpace NTSCRgb = new GammaWorkingSpace(2.2F, Illuminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + + /// + /// PAL/SECAM Rgb working space. + /// + public static readonly RgbWorkingSpace PALSECAMRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + + /// + /// ProPhoto Rgb working space. + /// + public static readonly RgbWorkingSpace ProPhotoRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); + + /// + /// SMPTE-C Rgb working space. + /// + public static readonly RgbWorkingSpace SMPTECRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + + /// + /// Wide Gamut Rgb working space. + /// + public static readonly RgbWorkingSpace WideGamutRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); +} diff --git a/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs index 0505395de..4719750b7 100644 --- a/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs +++ b/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs @@ -19,16 +19,25 @@ public static class VonKriesChromaticAdaptation /// /// Performs a linear transformation of a source color in to the destination color. /// + /// The type of color profile to convert from. + /// The type of color profile to convert to. /// Doesn't crop the resulting color space coordinates (e.g. allows negative values for XYZ coordinates). /// The color profile conversion options. /// The source color. /// The - public static CieXyz Transform(ColorConversionOptions options, in CieXyz source) + public static CieXyz Transform(ColorConversionOptions options, in CieXyz source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile { - CieXyz sourceWhitePoint = options.WhitePoint; - CieXyz destinationWhitePoint = options.TargetWhitePoint; + CieXyz sourceWhitePoint = TFrom.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint + ? options.WhitePoint + : options.RgbWorkingSpace.WhitePoint; - if (sourceWhitePoint.Equals(destinationWhitePoint)) + CieXyz targetWhitePoint = TTo.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint + ? options.TargetWhitePoint + : options.TargetRgbWorkingSpace.WhitePoint; + + if (sourceWhitePoint.Equals(targetWhitePoint)) { return new(source.X, source.Y, source.Z); } @@ -37,7 +46,7 @@ public static class VonKriesChromaticAdaptation Vector3 sourceColorLms = Vector3.Transform(source.ToVector3(), matrix); Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix); - Vector3 targetWhitePointLms = Vector3.Transform(destinationWhitePoint.ToVector3(), matrix); + Vector3 targetWhitePointLms = Vector3.Transform(targetWhitePoint.ToVector3(), matrix); Vector3 vector = targetWhitePointLms / sourceWhitePointLms; Vector3 targetColorLms = Vector3.Multiply(vector, sourceColorLms); @@ -49,19 +58,28 @@ public static class VonKriesChromaticAdaptation /// /// Performs a bulk linear transformation of a source color in to the destination color. /// + /// The type of color profile to convert from. + /// The type of color profile to convert to. /// Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates). /// The color profile conversion options. /// The span to the source colors. /// The span to the destination colors. - public static void Transform(ColorConversionOptions options, ReadOnlySpan source, Span destination) + public static void Transform(ColorConversionOptions options, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); int count = source.Length; - CieXyz sourceWhitePoint = options.WhitePoint; - CieXyz destinationWhitePoint = options.TargetWhitePoint; + CieXyz sourceWhitePoint = TFrom.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint + ? options.WhitePoint + : options.RgbWorkingSpace.WhitePoint; + + CieXyz targetWhitePoint = TTo.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint + ? options.TargetWhitePoint + : options.TargetRgbWorkingSpace.WhitePoint; - if (sourceWhitePoint.Equals(destinationWhitePoint)) + if (sourceWhitePoint.Equals(targetWhitePoint)) { source.CopyTo(destination[..count]); return; @@ -73,16 +91,18 @@ public static class VonKriesChromaticAdaptation ref CieXyz sourceBase = ref MemoryMarshal.GetReference(source); ref CieXyz destinationBase = ref MemoryMarshal.GetReference(destination); + Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix); + Vector3 targetWhitePointLms = Vector3.Transform(targetWhitePoint.ToVector3(), matrix); + + Vector3 vector = targetWhitePointLms / sourceWhitePointLms; + for (nuint i = 0; i < (uint)count; i++) { ref CieXyz sp = ref Unsafe.Add(ref sourceBase, i); ref CieXyz dp = ref Unsafe.Add(ref destinationBase, i); Vector3 sourceColorLms = Vector3.Transform(sp.ToVector3(), matrix); - Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix); - Vector3 targetWhitePointLms = Vector3.Transform(destinationWhitePoint.ToVector3(), matrix); - Vector3 vector = targetWhitePointLms / sourceWhitePointLms; Vector3 targetColorLms = Vector3.Multiply(vector, sourceColorLms); dp = new CieXyz(Vector3.Transform(targetColorLms, inverseMatrix)); } diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs new file mode 100644 index 000000000..91fa42624 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/GammaWorkingSpace.cs @@ -0,0 +1,68 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Companding; + +namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +/// +/// The gamma working space. +/// +public sealed class GammaWorkingSpace : RgbWorkingSpace +{ + /// + /// Initializes a new instance of the class. + /// + /// The gamma value. + /// The reference white point. + /// The chromaticity of the rgb primaries. + public GammaWorkingSpace(float gamma, CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) => this.Gamma = gamma; + + /// + /// Gets the gamma value. + /// + public float Gamma { get; } + + /// + public override void Compress(Span vectors) => GammaCompanding.Compress(vectors, this.Gamma); + + /// + public override void Expand(Span vectors) => GammaCompanding.Expand(vectors, this.Gamma); + + /// + public override Vector4 Compress(Vector4 vector) => GammaCompanding.Compress(vector, this.Gamma); + + /// + public override Vector4 Expand(Vector4 vector) => GammaCompanding.Expand(vector, this.Gamma); + + /// + public override bool Equals(object? obj) + { + if (obj is null) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj is GammaWorkingSpace other) + { + return this.Gamma.Equals(other.Gamma) + && this.WhitePoint.Equals(other.WhitePoint) + && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); + } + + return false; + } + + /// + public override int GetHashCode() => HashCode.Combine( + this.WhitePoint, + this.ChromaticityCoordinates, + this.Gamma); +} diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/LWorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/LWorkingSpace.cs new file mode 100644 index 000000000..77dc2c06d --- /dev/null +++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/LWorkingSpace.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Companding; + +namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +/// +/// L* working space. +/// +public sealed class LWorkingSpace : RgbWorkingSpace +{ + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public LWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) + { + } + + /// + public override void Compress(Span vectors) => LCompanding.Compress(vectors); + + /// + public override void Expand(Span vectors) => LCompanding.Expand(vectors); + + /// + public override Vector4 Compress(Vector4 vector) => LCompanding.Compress(vector); + + /// + public override Vector4 Expand(Vector4 vector) => LCompanding.Expand(vector); +} diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec2020WorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec2020WorkingSpace.cs new file mode 100644 index 000000000..970d10302 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec2020WorkingSpace.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Companding; + +namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +/// +/// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. +/// +public sealed class Rec2020WorkingSpace : RgbWorkingSpace +{ + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public Rec2020WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) + { + } + + /// + public override void Compress(Span vectors) => Rec2020Companding.Compress(vectors); + + /// + public override void Expand(Span vectors) => Rec2020Companding.Expand(vectors); + + /// + public override Vector4 Compress(Vector4 vector) => Rec2020Companding.Compress(vector); + + /// + public override Vector4 Expand(Vector4 vector) => Rec2020Companding.Expand(vector); +} diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec709WorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec709WorkingSpace.cs new file mode 100644 index 000000000..011da5807 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/Rec709WorkingSpace.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Companding; + +namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +/// +/// Rec. 709 (ITU-R Recommendation BT.709) working space. +/// +public sealed class Rec709WorkingSpace : RgbWorkingSpace +{ + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public Rec709WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) + { + } + + /// + public override void Compress(Span vectors) => Rec709Companding.Compress(vectors); + + /// + public override void Expand(Span vectors) => Rec709Companding.Expand(vectors); + + /// + public override Vector4 Compress(Vector4 vector) => Rec709Companding.Compress(vector); + + /// + public override Vector4 Expand(Vector4 vector) => Rec709Companding.Expand(vector); +} diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs new file mode 100644 index 000000000..97069b856 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/RgbWorkingSpace.cs @@ -0,0 +1,85 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; + +namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +/// +/// Base class for all implementations of . +/// +public abstract class RgbWorkingSpace +{ + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + protected RgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + { + this.WhitePoint = referenceWhite; + this.ChromaticityCoordinates = chromaticityCoordinates; + } + + /// + /// Gets the reference white point + /// + public CieXyz WhitePoint { get; } + + /// + /// Gets the chromaticity of the rgb primaries. + /// + public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } + + /// + /// Compresses the linear vectors to their nonlinear equivalents with respect to the energy. + /// + /// The span of vectors. + public abstract void Compress(Span vectors); + + /// + /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. + /// + /// The span of vectors. + public abstract void Expand(Span vectors); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public abstract Vector4 Compress(Vector4 vector); + + /// + /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// + /// The vector. + /// The . + public abstract Vector4 Expand(Vector4 vector); + + /// + public override bool Equals(object? obj) + { + if (obj is null) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj is RgbWorkingSpace other) + { + return this.WhitePoint.Equals(other.WhitePoint) + && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); + } + + return false; + } + + /// + public override int GetHashCode() + => HashCode.Combine(this.WhitePoint, this.ChromaticityCoordinates); +} diff --git a/src/ImageSharp/ColorProfiles/WorkingSpaces/SRgbWorkingSpace.cs b/src/ImageSharp/ColorProfiles/WorkingSpaces/SRgbWorkingSpace.cs new file mode 100644 index 000000000..b88e6c898 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/WorkingSpaces/SRgbWorkingSpace.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Companding; + +namespace SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; + +/// +/// The sRgb working space. +/// +public sealed class SRgbWorkingSpace : RgbWorkingSpace +{ + /// + /// Initializes a new instance of the class. + /// + /// The reference white point. + /// The chromaticity of the rgb primaries. + public SRgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) + : base(referenceWhite, chromaticityCoordinates) + { + } + + /// + public override void Compress(Span vectors) => SRgbCompanding.Compress(vectors); + + /// + public override void Expand(Span vectors) => SRgbCompanding.Expand(vectors); + + /// + public override Vector4 Compress(Vector4 vector) => SRgbCompanding.Compress(vector); + + /// + public override Vector4 Expand(Vector4 vector) => SRgbCompanding.Expand(vector); +} From 66bbc6af24192da328b359ca095cdc2ecb8ba239 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 20 Mar 2024 12:32:28 +1000 Subject: [PATCH 04/79] Remove clamping --- src/ImageSharp/ColorProfiles/Rgb.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs index 2669d2c96..bccae1132 100644 --- a/src/ImageSharp/ColorProfiles/Rgb.cs +++ b/src/ImageSharp/ColorProfiles/Rgb.cs @@ -9,19 +9,18 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal readonly struct Rgb : IProfileConnectingSpace { - private static readonly Vector3 Min = Vector3.Zero; - private static readonly Vector3 Max = Vector3.One; - /// /// Initializes a new instance of the struct. /// - /// The red component ranging between 0 and 1. - /// The green component ranging between 0 and 1. - /// The blue component ranging between 0 and 1. + /// The red component usually ranging between 0 and 1. + /// The green component usually ranging between 0 and 1. + /// The blue component usually ranging between 0 and 1. [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb(float r, float g, float b) - : this(new Vector3(r, g, b)) { + this.R = r; + this.G = g; + this.B = b; } /// @@ -31,7 +30,6 @@ internal readonly struct Rgb : IProfileConnectingSpace [MethodImpl(MethodImplOptions.AggressiveInlining)] private Rgb(Vector3 source) { - source = Vector3.Clamp(source, Min, Max); this.R = source.X; this.G = source.Y; this.B = source.Z; @@ -165,7 +163,7 @@ internal readonly struct Rgb : IProfileConnectingSpace /// /// Expands the color into a generic ("scaled") representation - /// with values scaled and clamped between 0 and 1. + /// with values scaled and usually clamped between 0 and 1. /// The vector components are typically expanded in least to greatest significance order. /// /// The . @@ -174,7 +172,7 @@ internal readonly struct Rgb : IProfileConnectingSpace /// /// Expands the color into a generic ("scaled") representation - /// with values scaled and clamped between 0 and 1. + /// with values scaled and usually clamped between 0 and 1. /// The vector components are typically expanded in least to greatest significance order. /// /// The . From cb71ae24360fb0a41b1d3e8529cfc6dd9239b6d0 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 20 Mar 2024 17:44:50 +1000 Subject: [PATCH 05/79] Complete Rgb conversion --- ...rProfileConverterExtensionsCieLabCieXyz.cs | 2 +- ...rProfileConverterExtensionsCieXyzCieLab.cs | 2 +- ...rProfileConverterExtensionsCieXyzCieXyz.cs | 14 +- .../Companding/CompandingUtilities.cs | 19 ++- .../Companding/GammaCompanding.cs | 10 +- .../ColorProfiles/Companding/LCompanding.cs | 10 +- .../Companding/Rec2020Companding.cs | 10 +- .../Companding/Rec709Companding.cs | 10 +- .../Companding/SRgbCompanding.cs | 10 +- .../ApproximateColorProfileComparer.cs | 7 +- .../RgbAndCieXyzConversionTest.cs | 151 ++++++++++++++++++ 11 files changed, 202 insertions(+), 43 deletions(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs index 8860777cd..cf9f91801 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs @@ -21,7 +21,7 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz CieXyz pcsTo = pcsFrom.ToProfileConnectingSpace(options); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, in pcsTo); + pcsTo = VonKriesChromaticAdaptation.Transform(options, in pcsTo); // Convert to output from PCS return TTo.FromProfileConnectingSpace(options, pcsTo); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs index 50317311c..a73677129 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs @@ -18,7 +18,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab CieXyz pcsFrom = source.ToProfileConnectingSpace(options); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + pcsFrom = VonKriesChromaticAdaptation.Transform(options, in pcsFrom); // Convert between PCS CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFrom); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs index 2380bb15e..fd6ff21e4 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs @@ -18,13 +18,10 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz CieXyz pcsFrom = source.ToProfileConnectingSpace(options); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, in pcsFrom); - - // Convert between PCS - CieXyz pcsTo = CieXyz.FromProfileConnectingSpace(options, in pcsFrom); + pcsFrom = VonKriesChromaticAdaptation.Transform(options, in pcsFrom); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, pcsFrom); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) @@ -41,12 +38,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz // Adapt to target white point VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); - // Convert between PCS. - using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); - Span pcsTo = pcsToOwner.GetSpan(); - CieXyz.FromProfileConnectionSpace(options, pcsFrom, pcsTo); - // Convert to output from PCS - TTo.FromProfileConnectionSpace(options, pcsTo, destination); + TTo.FromProfileConnectionSpace(options, pcsFrom, destination); } } diff --git a/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs b/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs index 44dae35a9..1970e2d94 100644 --- a/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs +++ b/src/ImageSharp/ColorProfiles/Companding/CompandingUtilities.cs @@ -17,17 +17,28 @@ public static class CompandingUtilities { private const int Length = Scale + 2; // 256kb @ 16bit precision. private const int Scale = (1 << 16) - 1; - private static readonly ConcurrentDictionary<(Type, double), Lazy> LookupTables = new(); + private static readonly ConcurrentDictionary<(Type, double), float[]> CompressLookupTables = new(); + private static readonly ConcurrentDictionary<(Type, double), float[]> ExpandLookupTables = new(); /// - /// Lazily creates and stores a companding lookup table using the given function and modifier. + /// Lazily creates and stores a companding compression lookup table using the given function and modifier. /// /// The type of companding function. /// The companding function. /// A modifier to pass to the function. /// The array. - public static Lazy GetLookupTable(Func compandingFunction, double modifier = 0) - => LookupTables.GetOrAdd((typeof(T), modifier), args => new(() => CreateLookupTableImpl(compandingFunction, args.Item2), true)); + public static float[] GetCompressLookupTable(Func compandingFunction, double modifier = 0) + => CompressLookupTables.GetOrAdd((typeof(T), modifier), args => CreateLookupTableImpl(compandingFunction, args.Item2)); + + /// + /// Lazily creates and stores a companding expanding lookup table using the given function and modifier. + /// + /// The type of companding function. + /// The companding function. + /// A modifier to pass to the function. + /// The array. + public static float[] GetExpandLookupTable(Func compandingFunction, double modifier = 0) + => ExpandLookupTables.GetOrAdd((typeof(T), modifier), args => CreateLookupTableImpl(compandingFunction, args.Item2)); /// /// Creates a companding lookup table using the given function. diff --git a/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs index 28e9a4641..34ca8bf5e 100644 --- a/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs +++ b/src/ImageSharp/ColorProfiles/Companding/GammaCompanding.cs @@ -24,7 +24,7 @@ public static class GammaCompanding /// The span of vectors. /// The gamma value. public static void Compress(Span vectors, double gamma) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction, gamma).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetCompressLookupTable(CompressFunction, gamma)); /// /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. @@ -32,7 +32,7 @@ public static class GammaCompanding /// The span of vectors. /// The gamma value. public static void Expand(Span vectors, double gamma) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction, gamma).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetExpandLookupTable(ExpandFunction, gamma)); /// /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. @@ -41,16 +41,16 @@ public static class GammaCompanding /// The gamma value. /// The . public static Vector4 Compress(Vector4 vector, double gamma) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction, gamma).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetCompressLookupTable(CompressFunction, gamma)); /// - /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// Expands the nonlinear vector to its linear equivalent with respect to the energy. /// /// The vector. /// The gamma value. /// The . public static Vector4 Expand(Vector4 vector, double gamma) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction, gamma).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetExpandLookupTable(ExpandFunction, gamma)); private class GammaCompandingKey; } diff --git a/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs index 79349d1c5..4f5383038 100644 --- a/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs +++ b/src/ImageSharp/ColorProfiles/Companding/LCompanding.cs @@ -42,14 +42,14 @@ public static class LCompanding /// /// The span of vectors. public static void Compress(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. /// /// The span of vectors. public static void Expand(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); /// /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. @@ -57,15 +57,15 @@ public static class LCompanding /// The vector. /// The . public static Vector4 Compress(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// - /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// Expands the nonlinear vector to its linear equivalent with respect to the energy. /// /// The vector. /// The . public static Vector4 Expand(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); private class LCompandingKey; } diff --git a/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs b/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs index 9beab3a86..1901e6471 100644 --- a/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs +++ b/src/ImageSharp/ColorProfiles/Companding/Rec2020Companding.cs @@ -46,14 +46,14 @@ public static class Rec2020Companding /// /// The span of vectors. public static void Compress(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. /// /// The span of vectors. public static void Expand(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); /// /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. @@ -61,15 +61,15 @@ public static class Rec2020Companding /// The vector. /// The . public static Vector4 Compress(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// - /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// Expands the nonlinear vector to its linear equivalent with respect to the energy. /// /// The vector. /// The . public static Vector4 Expand(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); private class Rec2020CompandingKey; } diff --git a/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs b/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs index 93a32672c..94b17d8d0 100644 --- a/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs +++ b/src/ImageSharp/ColorProfiles/Companding/Rec709Companding.cs @@ -42,14 +42,14 @@ public static class Rec709Companding /// /// The span of vectors. public static void Compress(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. /// /// The span of vectors. public static void Expand(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); /// /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. @@ -57,15 +57,15 @@ public static class Rec709Companding /// The vector. /// The . public static Vector4 Compress(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// - /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// Expands the nonlinear vector to its linear equivalent with respect to the energy. /// /// The vector. /// The . public static Vector4 Expand(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); private class Rec2020CompandingKey; } diff --git a/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs index 04e4007a8..ab2710230 100644 --- a/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs +++ b/src/ImageSharp/ColorProfiles/Companding/SRgbCompanding.cs @@ -42,14 +42,14 @@ public static class SRgbCompanding /// /// The span of vectors. public static void Compress(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// /// Expands the nonlinear vectors to their linear equivalents with respect to the energy. /// /// The span of vectors. public static void Expand(Span vectors) - => CompandingUtilities.Compand(vectors, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vectors, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); /// /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. @@ -57,15 +57,15 @@ public static class SRgbCompanding /// The vector. /// The . public static Vector4 Compress(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(CompressFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetCompressLookupTable(CompressFunction)); /// - /// Compresses the linear vector to its nonlinear equivalent with respect to the energy. + /// Expands the nonlinear vector to its linear equivalent with respect to the energy. /// /// The vector. /// The . public static Vector4 Expand(Vector4 vector) - => CompandingUtilities.Compand(vector, CompandingUtilities.GetLookupTable(ExpandFunction).Value); + => CompandingUtilities.Compand(vector, CompandingUtilities.GetExpandLookupTable(ExpandFunction)); private class SRgbCompandingKey; } diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs index 5c2f9afbd..84319b66c 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -13,7 +13,8 @@ internal readonly struct ApproximateColorProfileComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer { private readonly float epsilon; @@ -31,6 +32,8 @@ internal readonly struct ApproximateColorProfileComparer : public bool Equals(CieLch x, CieLch y) => this.Equals(x.L, y.L) && this.Equals(x.C, y.C) && this.Equals(x.H, y.H); + public bool Equals(Rgb x, Rgb y) => this.Equals(x.R, y.R) && this.Equals(x.G, y.G) && this.Equals(x.B, y.B); + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); @@ -39,6 +42,8 @@ internal readonly struct ApproximateColorProfileComparer : public int GetHashCode([DisallowNull] CieLch obj) => obj.GetHashCode(); + public int GetHashCode([DisallowNull] Rgb obj) => obj.GetHashCode(); + private bool Equals(float x, float y) { float d = x - y; diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs new file mode 100644 index 000000000..5e867178a --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs @@ -0,0 +1,151 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ColorProfiles; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class RgbAndCieXyzConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001F); + + [Theory] + [InlineData(0.96422, 1.00000, 0.82521, 1, 1, 1)] + [InlineData(0.00000, 1.00000, 0.00000, 0, 1, 0)] + [InlineData(0.96422, 0.00000, 0.00000, 1, 0, 0.292064)] + [InlineData(0.00000, 0.00000, 0.82521, 0, 0.181415, 1)] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.297676, 0.267854, 0.045504, 0.720315, 0.509999, 0.168112)] + public void Convert_XYZ_D50_to_SRGB(float x, float y, float z, float r, float g, float b) + { + // Arrange + CieXyz input = new(x, y, z); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorProfileConverter converter = new(options); + Rgb expected = new(r, g, b); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0.950470, 1.000000, 1.088830, 1, 1, 1)] + [InlineData(0, 1.000000, 0, 0, 1, 0)] + [InlineData(0.950470, 0, 0, 1, 0, 0.254967)] + [InlineData(0, 0, 1.088830, 0, 0.235458, 1)] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.297676, 0.267854, 0.045504, 0.754903, 0.501961, 0.099998)] + public void Convert_XYZ_D65_to_SRGB(float x, float y, float z, float r, float g, float b) + { + // Arrange + CieXyz input = new(x, y, z); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorProfileConverter converter = new(options); + Rgb expected = new(r, g, b); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(1, 1, 1, 0.964220, 1.000000, 0.825210)] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 0, 0, 0.436075, 0.222504, 0.013932)] + [InlineData(0, 1, 0, 0.385065, 0.716879, 0.0971045)] + [InlineData(0, 0, 1, 0.143080, 0.060617, 0.714173)] + [InlineData(0.754902, 0.501961, 0.100000, 0.315757, 0.273323, 0.035506)] + public void Convert_SRGB_to_XYZ_D50(float r, float g, float b, float x, float y, float z) + { + // Arrange + Rgb input = new(r, g, b); + ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D50, RgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorProfileConverter converter = new(options); + CieXyz expected = new(x, y, z); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(1, 1, 1, 0.950470, 1.000000, 1.088830)] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 0, 0, 0.412456, 0.212673, 0.019334)] + [InlineData(0, 1, 0, 0.357576, 0.715152, 0.119192)] + [InlineData(0, 0, 1, 0.1804375, 0.072175, 0.950304)] + [InlineData(0.754902, 0.501961, 0.100000, 0.297676, 0.267854, 0.045504)] + public void Convert_SRGB_to_XYZ_D65(float r, float g, float b, float x, float y, float z) + { + // Arrange + Rgb input = new(r, g, b); + ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D65, RgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorProfileConverter converter = new(options); + CieXyz expected = new(x, y, z); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 80dc3f51d452894c664341f05fb3ac0aca1b1e14 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 20 Mar 2024 20:44:33 +1000 Subject: [PATCH 06/79] Don't over allocate --- .../ColorProfileConverterExtensionsCieLabCieXyz.cs | 4 ++-- src/ImageSharp/ColorProfiles/IColorProfile.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs index cf9f91801..545b5dadf 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs @@ -39,8 +39,8 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz TFrom.ToProfileConnectionSpace(options, source, pcsFrom); // Convert between PCS. - using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length * 2); - Span pcsTo = pcsToOwner.GetSpan()[..source.Length]; + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsTo = pcsToOwner.GetSpan(); CieLab.ToProfileConnectionSpace(options, pcsFrom, pcsTo); // Adapt to target white point diff --git a/src/ImageSharp/ColorProfiles/IColorProfile.cs b/src/ImageSharp/ColorProfiles/IColorProfile.cs index ac4b425fe..6a1b2ee8d 100644 --- a/src/ImageSharp/ColorProfiles/IColorProfile.cs +++ b/src/ImageSharp/ColorProfiles/IColorProfile.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.ColorProfiles; @@ -11,7 +11,7 @@ public interface IColorProfile /// /// Gets the chromatic adaption white point source. /// - /// The . + /// The . public static abstract ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource(); } From 07e354dca1a3aee0d429e0b5e7fd20f84a933e22 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 2 Apr 2024 21:26:53 +1000 Subject: [PATCH 07/79] Add YCbCr conversion --- ...olorProfileConverterExtensionsCieXyzRgb.cs | 52 ++++++ ...olorProfileConverterExtensionsRgbCieXyz.cs | 52 ++++++ src/ImageSharp/ColorProfiles/Rgb.cs | 5 +- src/ImageSharp/ColorProfiles/YCbCr.cs | 158 ++++++++++++++++++ .../ApproximateColorProfileComparer.cs | 7 +- .../RgbAndYCbCrConversionTest.cs | 76 +++++++++ 6 files changed, 348 insertions(+), 2 deletions(-) create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs create mode 100644 src/ImageSharp/ColorProfiles/YCbCr.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs new file mode 100644 index 000000000..b0ec427c5 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsCieXyzRgb +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + CieXyz pcsFrom = source.ToProfileConnectingSpace(options); + + // Adapt to target white point + pcsFrom = VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + + // Convert between PCS + Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFrom); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFrom = pcsFromOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFrom); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + + // Convert between PCS. + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsTo = pcsToOwner.GetSpan(); + Rgb.FromProfileConnectionSpace(options, pcsFrom, pcsTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsTo, destination); + } +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs new file mode 100644 index 000000000..f98d4bfc3 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsRgbCieXyz +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + Rgb pcsFrom = source.ToProfileConnectingSpace(options); + + // Convert between PCS + CieXyz pcsTo = pcsFrom.ToProfileConnectingSpace(options); + + // Adapt to target white point + pcsTo = VonKriesChromaticAdaptation.Transform(options, in pcsTo); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFrom = pcsFromOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFrom); + + // Convert between PCS. + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsTo = pcsToOwner.GetSpan(); + Rgb.ToProfileConnectionSpace(options, pcsFrom, pcsTo); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsTo, pcsTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsTo, destination); + } +} diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs index bccae1132..21575dd85 100644 --- a/src/ImageSharp/ColorProfiles/Rgb.cs +++ b/src/ImageSharp/ColorProfiles/Rgb.cs @@ -7,7 +7,10 @@ using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; namespace SixLabors.ImageSharp.ColorProfiles; -internal readonly struct Rgb : IProfileConnectingSpace +/// +/// Represents an RGB (red, green, blue) color profile. +/// +public readonly struct Rgb : IProfileConnectingSpace { /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/ColorProfiles/YCbCr.cs b/src/ImageSharp/ColorProfiles/YCbCr.cs new file mode 100644 index 000000000..07062b32a --- /dev/null +++ b/src/ImageSharp/ColorProfiles/YCbCr.cs @@ -0,0 +1,158 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents an YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification for the JFIF use with Jpeg. +/// +/// +/// +public readonly struct YCbCr : IColorProfile +{ + private static readonly Vector3 Min = Vector3.Zero; + private static readonly Vector3 Max = new(255); + + /// + /// Initializes a new instance of the struct. + /// + /// The y luminance component. + /// The cb chroma component. + /// The cr chroma component. + [MethodImpl(InliningOptions.ShortMethod)] + public YCbCr(float y, float cb, float cr) + : this(new Vector3(y, cb, cr)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the y, cb, cr components. + [MethodImpl(InliningOptions.ShortMethod)] + public YCbCr(Vector3 vector) + { + vector = Vector3.Clamp(vector, Min, Max); + this.Y = vector.X; + this.Cb = vector.Y; + this.Cr = vector.Z; + } + + /// + /// Gets the Y luminance component. + /// A value ranging between 0 and 255. + /// + public readonly float Y { get; } + + /// + /// Gets the Cb chroma component. + /// A value ranging between 0 and 255. + /// + public readonly float Cb { get; } + + /// + /// Gets the Cr chroma component. + /// A value ranging between 0 and 255. + /// + public readonly float Cr { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + public static bool operator ==(YCbCr left, YCbCr right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(YCbCr left, YCbCr right) => !left.Equals(right); + + /// + public static YCbCr FromProfileConnectingSpace(ColorConversionOptions options, in Rgb source) + { + Vector3 rgb = source.ToScaledVector3() * Max; + float r = rgb.X; + float g = rgb.Y; + float b = rgb.Z; + + float y = (0.299F * r) + (0.587F * g) + (0.114F * b); + float cb = 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b)); + float cr = 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b)); + + return new YCbCr(y, cb, cr); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + // TODO: We can optimize this by using SIMD + for (int i = 0; i < source.Length; i++) + { + Rgb rgb = source[i]; + destination[i] = FromProfileConnectingSpace(options, in rgb); + } + } + + /// + public Rgb ToProfileConnectingSpace(ColorConversionOptions options) + { + float y = this.Y; + float cb = this.Cb - 128F; + float cr = this.Cr - 128F; + + float r = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero); + float g = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero); + float b = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero); + + return Rgb.FromScaledVector3(new Vector3(r, g, b) / Max); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + // TODO: We can optimize this by using SIMD + for (int i = 0; i < source.Length; i++) + { + YCbCr ycbcr = source[i]; + destination[i] = ycbcr.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => HashCode.Combine(this.Y, this.Cb, this.Cr); + + /// + public override string ToString() => FormattableString.Invariant($"YCbCr({this.Y}, {this.Cb}, {this.Cr})"); + + /// + public override bool Equals(object? obj) => obj is YCbCr other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(YCbCr other) + => this.Y.Equals(other.Y) + && this.Cb.Equals(other.Cb) + && this.Cr.Equals(other.Cr); +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs index 84319b66c..921148318 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -14,7 +14,8 @@ internal readonly struct ApproximateColorProfileComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer { private readonly float epsilon; @@ -34,6 +35,8 @@ internal readonly struct ApproximateColorProfileComparer : public bool Equals(Rgb x, Rgb y) => this.Equals(x.R, y.R) && this.Equals(x.G, y.G) && this.Equals(x.B, y.B); + public bool Equals(YCbCr x, YCbCr y) => this.Equals(x.Y, y.Y) && this.Equals(x.Cb, y.Cb) && this.Equals(x.Cr, y.Cr); + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); @@ -44,6 +47,8 @@ internal readonly struct ApproximateColorProfileComparer : public int GetHashCode([DisallowNull] Rgb obj) => obj.GetHashCode(); + public int GetHashCode([DisallowNull] YCbCr obj) => obj.GetHashCode(); + private bool Equals(float x, float y) { float d = x - y; diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs new file mode 100644 index 000000000..05c17e89e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs @@ -0,0 +1,76 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated mathematically +/// +public class RgbAndYCbCrConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.001F); + + [Theory] + [InlineData(255, 128, 128, 1, 1, 1)] + [InlineData(0, 128, 128, 0, 0, 0)] + [InlineData(128, 128, 128, 0.502, 0.502, 0.502)] + public void Convert_YCbCr_To_Rgb(float y, float cb, float cr, float r, float g, float b) + { + // Arrange + YCbCr input = new(y, cb, cr); + Rgb expected = new(r, g, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(1, 1, 1, 255, 128, 128)] + [InlineData(0.5, 0.5, 0.5, 127.5, 128, 128)] + [InlineData(1, 0, 0, 76.245, 84.972, 255)] + public void Convert_Rgb_To_YCbCr(float r, float g, float b, float y, float cb, float cr) + { + // Arrange + Rgb input = new(r, g, b); + YCbCr expected = new(y, cb, cr); + ColorProfileConverter converter = new(); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + YCbCr actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From e24146b0495ddd51b2c0ffd673c6cbf96d5f2a58 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 4 Apr 2024 12:10:33 +1000 Subject: [PATCH 08/79] Add Add LUV and LCHLUV --- src/ImageSharp/ColorProfiles/CieConstants.cs | 4 +- src/ImageSharp/ColorProfiles/CieLchuv.cs | 191 ++++++++++++++++ src/ImageSharp/ColorProfiles/CieLuv.cs | 211 ++++++++++++++++++ src/ImageSharp/ColorProfiles/YCbCr.cs | 10 +- src/ImageSharp/Common/Helpers/Numerics.cs | 8 + .../ApproximateColorProfileComparer.cs | 12 +- .../CieLchuvAndCieLuvConversionTests.cs | 89 ++++++++ 7 files changed, 517 insertions(+), 8 deletions(-) create mode 100644 src/ImageSharp/ColorProfiles/CieLchuv.cs create mode 100644 src/ImageSharp/ColorProfiles/CieLuv.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs diff --git a/src/ImageSharp/ColorProfiles/CieConstants.cs b/src/ImageSharp/ColorProfiles/CieConstants.cs index f4b74eaa1..d13a84450 100644 --- a/src/ImageSharp/ColorProfiles/CieConstants.cs +++ b/src/ImageSharp/ColorProfiles/CieConstants.cs @@ -12,10 +12,10 @@ internal static class CieConstants /// /// 216F / 24389F /// - public const float Epsilon = 0.008856452F; + public const float Epsilon = 216f / 24389f; /// /// 24389F / 27F /// - public const float Kappa = 903.2963F; + public const float Kappa = 24389f / 27f; } diff --git a/src/ImageSharp/ColorProfiles/CieLchuv.cs b/src/ImageSharp/ColorProfiles/CieLchuv.cs new file mode 100644 index 000000000..f8f62d104 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieLchuv.cs @@ -0,0 +1,191 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color. +/// +/// +public readonly struct CieLchuv : IColorProfile +{ + private static readonly Vector3 Min = new(0, -200, 0); + private static readonly Vector3 Max = new(100, 200, 360); + + /// + /// Initializes a new instance of the struct. + /// + /// The lightness dimension. + /// The chroma, relative saturation. + /// The hue in degrees. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLchuv(float l, float c, float h) + : this(new Vector3(l, c, h)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l, c, h components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLchuv(Vector3 vector) + : this() + { + vector = Vector3.Clamp(vector, Min, Max); + this.L = vector.X; + this.C = vector.Y; + this.H = vector.Z; + } + + /// + /// Gets the lightness dimension. + /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). + /// + public readonly float L { get; } + + /// + /// Gets the a chroma component. + /// A value ranging from 0 to 200. + /// + public readonly float C { get; } + + /// + /// Gets the h° hue component in degrees. + /// A value ranging from 0 to 360. + /// + public readonly float H { get; } + + /// + /// Gets the reference white point of this color + /// + public readonly CieXyz WhitePoint { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + public static bool operator ==(CieLchuv left, CieLchuv right) => left.Equals(right); + + /// + /// Compares two objects for inequality + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + public static bool operator !=(CieLchuv left, CieLchuv right) => !left.Equals(right); + + /// + public static CieLchuv FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + { + CieLuv luv = CieLuv.FromProfileConnectingSpace(options, source); + + // Conversion algorithm described here: + // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 + float l = luv.L, u = luv.U, v = luv.V; + float c = MathF.Sqrt((u * u) + (v * v)); + float hRadians = MathF.Atan2(v, u); + float hDegrees = GeometryUtilities.RadianToDegree(hRadians); + + // Wrap the angle round at 360. + hDegrees %= 360; + + // Make sure it's not negative. + while (hDegrees < 0) + { + hDegrees += 360; + } + + return new CieLchuv(l, c, hDegrees); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span 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); + } + } + + /// + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + { + // Conversion algorithm described here: + // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 + float l = this.L, c = this.C, hDegrees = this.H; + float hRadians = GeometryUtilities.DegreeToRadian(hDegrees); + + float u = c * MathF.Cos(hRadians); + float v = c * MathF.Sin(hRadians); + + CieLuv luv = new(l, u, v); + return luv.ToProfileConnectingSpace(options); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + CieLchuv lch = source[i]; + destination[i] = lch.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + public override int GetHashCode() + => HashCode.Combine(this.L, this.C, this.H, this.WhitePoint); + + /// + public override string ToString() + => FormattableString.Invariant($"CieLchuv({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); + + /// + public override bool Equals(object? obj) + => obj is CieLchuv other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieLchuv other) + => this.L.Equals(other.L) + && this.C.Equals(other.C) + && this.H.Equals(other.H) + && this.WhitePoint.Equals(other.WhitePoint); + + /// + /// Computes the saturation of the color (chroma normalized by lightness) + /// + /// + /// A value ranging from 0 to 100. + /// + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float Saturation() + { + float result = 100 * (this.C / this.L); + + if (float.IsNaN(result)) + { + return 0; + } + + return result; + } +} diff --git a/src/ImageSharp/ColorProfiles/CieLuv.cs b/src/ImageSharp/ColorProfiles/CieLuv.cs new file mode 100644 index 000000000..8708ea255 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieLuv.cs @@ -0,0 +1,211 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// The CIE 1976 (L*, u*, v*) color space, commonly known by its abbreviation CIELUV, is a color space adopted by the International +/// Commission on Illumination (CIE) in 1976, as a simple-to-compute transformation of the 1931 CIE XYZ color space, but which +/// attempted perceptual uniformity +/// +/// +public readonly struct CieLuv : IColorProfile +{ + /// + /// Initializes a new instance of the struct. + /// + /// The lightness dimension. + /// The blue-yellow chromaticity coordinate of the given white point. + /// The red-green chromaticity coordinate of the given white point. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLuv(float l, float u, float v) + { + // Not clamping as documentation about this space only indicates "usual" ranges + this.L = l; + this.U = u; + this.V = v; + } + + /// + /// Gets the lightness dimension + /// A value usually ranging between 0 and 100. + /// + public readonly float L { get; } + + /// + /// Gets the blue-yellow chromaticity coordinate of the given white point. + /// A value usually ranging between -100 and 100. + /// + public readonly float U { get; } + + /// + /// Gets the red-green chromaticity coordinate of the given white point. + /// A value usually ranging between -100 and 100. + /// + public readonly float V { get; } + + /// + /// Gets the reference white point of this color + /// + public readonly CieXyz WhitePoint { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(CieLuv left, CieLuv right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(CieLuv left, CieLuv right) => !left.Equals(right); + + /// + public static CieLuv FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + { + // Use doubles here for accuracy. + // Conversion algorithm described here: + // http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Luv.html + CieXyz whitePoint = options.TargetWhitePoint; + + double yr = source.Y / whitePoint.Y; + + double den = source.X + (15 * source.Y) + (3 * source.Z); + double up = den > 0 ? ComputeU(in source) : 0; + double vp = den > 0 ? ComputeV(in source) : 0; + double upr = ComputeU(in whitePoint); + double vpr = ComputeV(in whitePoint); + + const double e = 1 / 3d; + double l = yr > CieConstants.Epsilon + ? ((116 * Math.Pow(yr, e)) - 16d) + : (CieConstants.Kappa * yr); + + if (double.IsNaN(l)) + { + l = 0; + } + + double u = 13 * l * (up - upr); + double v = 13 * l * (vp - vpr); + + if (double.IsNaN(u) || u == -0d) + { + u = 0; + } + + if (double.IsNaN(v) || u == -0d) + { + v = 0; + } + + return new CieLuv((float)l, (float)u, (float)v); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span 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); + } + } + + /// + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + { + // Use doubles here for accuracy. + // Conversion algorithm described here: + // http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html + CieXyz whitePoint = options.WhitePoint; + + double l = this.L, u = this.U, v = this.V; + + double u0 = ComputeU(in whitePoint); + double v0 = ComputeV(in whitePoint); + + double y = l > CieConstants.Kappa * CieConstants.Epsilon + ? Numerics.Pow3((l + 16) / 116d) + : l / CieConstants.Kappa; + + double a = ((52 * l / (u + (13 * l * u0))) - 1) / 3; + double b = -5 * y; + const double c = -1 / 3d; + double d = y * ((39 * l / (v + (13 * l * v0))) - 5); + + double x = (d - b) / (a - c); + double z = (x * a) + b; + + if (double.IsNaN(x)) + { + x = 0; + } + + if (double.IsNaN(y)) + { + y = 0; + } + + if (double.IsNaN(z)) + { + z = 0; + } + + return new CieXyz((float)x, (float)y, (float)z); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + CieLuv luv = source[i]; + destination[i] = luv.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + public override int GetHashCode() => HashCode.Combine(this.L, this.U, this.V, this.WhitePoint); + + /// + public override string ToString() => FormattableString.Invariant($"CieLuv({this.L:#0.##}, {this.U:#0.##}, {this.V:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is CieLuv other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieLuv other) + => this.L.Equals(other.L) + && this.U.Equals(other.U) + && this.V.Equals(other.V) + && this.WhitePoint.Equals(other.WhitePoint); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static double ComputeU(in CieXyz source) + => (4 * source.X) / (source.X + (15 * source.Y) + (3 * source.Z)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static double ComputeV(in CieXyz source) + => (9 * source.Y) / (source.X + (15 * source.Y) + (3 * source.Z)); +} diff --git a/src/ImageSharp/ColorProfiles/YCbCr.cs b/src/ImageSharp/ColorProfiles/YCbCr.cs index 07062b32a..5f0b4118a 100644 --- a/src/ImageSharp/ColorProfiles/YCbCr.cs +++ b/src/ImageSharp/ColorProfiles/YCbCr.cs @@ -22,7 +22,7 @@ public readonly struct YCbCr : IColorProfile /// The y luminance component. /// The cb chroma component. /// The cr chroma component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public YCbCr(float y, float cb, float cr) : this(new Vector3(y, cb, cr)) { @@ -32,7 +32,7 @@ public readonly struct YCbCr : IColorProfile /// Initializes a new instance of the struct. /// /// The vector representing the y, cb, cr components. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public YCbCr(Vector3 vector) { vector = Vector3.Clamp(vector, Min, Max); @@ -77,7 +77,7 @@ public readonly struct YCbCr : IColorProfile /// /// True if the current left is unequal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(YCbCr left, YCbCr right) => !left.Equals(right); /// @@ -140,7 +140,7 @@ public readonly struct YCbCr : IColorProfile => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => HashCode.Combine(this.Y, this.Cb, this.Cr); /// @@ -150,7 +150,7 @@ public readonly struct YCbCr : IColorProfile public override bool Equals(object? obj) => obj is YCbCr other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(YCbCr other) => this.Y.Equals(other.Y) && this.Cb.Equals(other.Cb) diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index 777de2dc9..ca14ae4c3 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.cs @@ -141,6 +141,14 @@ internal static class Numerics [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Pow3(float x) => x * x * x; + /// + /// Returns a specified number raised to the power of 3 + /// + /// A double-precision floating-point number + /// The number raised to the power of 3. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Pow3(double x) => x * x * x; + /// /// Implementation of 1D Gaussian G(x) function /// diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs index 921148318..f2c1d20a3 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -15,7 +15,9 @@ internal readonly struct ApproximateColorProfileComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer, + IEqualityComparer { private readonly float epsilon; @@ -37,6 +39,10 @@ internal readonly struct ApproximateColorProfileComparer : public bool Equals(YCbCr x, YCbCr y) => this.Equals(x.Y, y.Y) && this.Equals(x.Cb, y.Cb) && this.Equals(x.Cr, y.Cr); + public bool Equals(CieLchuv x, CieLchuv y) => this.Equals(x.L, y.L) && this.Equals(x.C, y.C) && this.Equals(x.H, y.H); + + public bool Equals(CieLuv x, CieLuv y) => this.Equals(x.L, y.L) && this.Equals(x.U, y.U) && this.Equals(x.V, y.V); + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); @@ -49,6 +55,10 @@ internal readonly struct ApproximateColorProfileComparer : public int GetHashCode([DisallowNull] YCbCr obj) => obj.GetHashCode(); + public int GetHashCode([DisallowNull] CieLchuv obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] CieLuv obj) => obj.GetHashCode(); + private bool Equals(float x, float y) { float d = x - y; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs new file mode 100644 index 000000000..a65c7e81c --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs @@ -0,0 +1,89 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieLchuvAndCieLuvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(54.2917, 106.8391, 40.8526, 54.2917, 80.8125, 69.8851)] + [InlineData(100, 0, 0, 100, 0, 0)] + [InlineData(100, 50, 180, 100, -50, 0)] + [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] + [InlineData(10, 36.0555, 123.6901, 10, -20, 30)] + [InlineData(10, 36.0555, 303.6901, 10, 20, -30)] + [InlineData(10, 36.0555, 236.3099, 10, -20, -30)] + public void Convert_CieLchuv_to_CieLuv(float l, float c, float h, float l2, float u, float v) + { + // Arrange + CieLchuv input = new(l, c, h); + CieLuv expected = new(l2, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(54.2917, 80.8125, 69.8851, 54.2917, 106.8391, 40.8526)] + [InlineData(100, 0, 0, 100, 0, 0)] + [InlineData(100, -50, 0, 100, 50, 180)] + [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] + [InlineData(10, -20, 30, 10, 36.0555, 123.6901)] + [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] + [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] + [InlineData(37.3511, 24.1720, 16.0684, 37.3511, 29.0255, 33.6141)] + public void Convert_CieLuv_to_CieLchuv(float l, float u, float v, float l2, float c, float h) + { + // Arrange + CieLuv input = new(l, u, v); + CieLchuv expected = new(l2, c, h); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; + + // Act + CieLchuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 7da1cae8de912d45621fde7f834d49726cbc4ab7 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 4 Apr 2024 13:15:34 +1000 Subject: [PATCH 09/79] Add XYY --- src/ImageSharp/ColorProfiles/CieXyy.cs | 156 ++++++++++++++++++ .../ApproximateColorProfileComparer.cs | 7 +- .../CieXyzAndCieXyyConversionTest.cs | 76 +++++++++ 3 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp/ColorProfiles/CieXyy.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs diff --git a/src/ImageSharp/ColorProfiles/CieXyy.cs b/src/ImageSharp/ColorProfiles/CieXyy.cs new file mode 100644 index 000000000..9a1953ac8 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/CieXyy.cs @@ -0,0 +1,156 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents an CIE xyY 1931 color +/// +/// +public readonly struct CieXyy : IColorProfile +{ + /// + /// Initializes a new instance of the struct. + /// + /// The x chroma component. + /// The y chroma component. + /// The y luminance component. + [MethodImpl(InliningOptions.ShortMethod)] + public CieXyy(float x, float y, float yl) + { + // Not clamping as documentation about this space only indicates "usual" ranges + this.X = x; + this.Y = y; + this.Yl = yl; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the x, y, Y components. + [MethodImpl(InliningOptions.ShortMethod)] + public CieXyy(Vector3 vector) + : this() + { + // Not clamping as documentation about this space only indicates "usual" ranges + this.X = vector.X; + this.Y = vector.Y; + this.Yl = vector.Z; + } + + /// + /// Gets the X chrominance component. + /// A value usually ranging between 0 and 1. + /// + public readonly float X { get; } + + /// + /// Gets the Y chrominance component. + /// A value usually ranging between 0 and 1. + /// + public readonly float Y { get; } + + /// + /// Gets the Y luminance component. + /// A value usually ranging between 0 and 1. + /// + public readonly float Yl { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(CieXyy left, CieXyy right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(CieXyy left, CieXyy right) => !left.Equals(right); + + /// + public static CieXyy FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + { + float x = source.X / (source.X + source.Y + source.Z); + float y = source.Y / (source.X + source.Y + source.Z); + + if (float.IsNaN(x) || float.IsNaN(y)) + { + return new CieXyy(0, 0, source.Y); + } + + return new CieXyy(x, y, source.Y); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span 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); + } + } + + /// + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + { + if (MathF.Abs(this.Y) < Constants.Epsilon) + { + return new CieXyz(0, 0, this.Yl); + } + + float x = (this.X * this.Yl) / this.Y; + float y = this.Yl; + float z = ((1 - this.X - this.Y) * y) / this.Y; + + return new CieXyz(x, y, z); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + CieXyy xyz = source[i]; + destination[i] = xyz.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + public override int GetHashCode() + => HashCode.Combine(this.X, this.Y, this.Yl); + + /// + public override string ToString() + => FormattableString.Invariant($"CieXyy({this.X:#0.##}, {this.Y:#0.##}, {this.Yl:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is CieXyy other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(CieXyy other) + => this.X.Equals(other.X) + && this.Y.Equals(other.Y) + && this.Yl.Equals(other.Yl); +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs index f2c1d20a3..81aac269d 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -17,7 +17,8 @@ internal readonly struct ApproximateColorProfileComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer { private readonly float epsilon; @@ -43,6 +44,8 @@ internal readonly struct ApproximateColorProfileComparer : public bool Equals(CieLuv x, CieLuv y) => this.Equals(x.L, y.L) && this.Equals(x.U, y.U) && this.Equals(x.V, y.V); + public bool Equals(CieXyy x, CieXyy y) => this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y) && this.Equals(x.Yl, y.Yl); + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); @@ -59,6 +62,8 @@ internal readonly struct ApproximateColorProfileComparer : public int GetHashCode([DisallowNull] CieLuv obj) => obj.GetHashCode(); + public int GetHashCode([DisallowNull] CieXyy obj) => obj.GetHashCode(); + private bool Equals(float x, float y) { float d = x - y; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs new file mode 100644 index 000000000..c4fa554a6 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs @@ -0,0 +1,76 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieXyzAndCieXyyConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001F); + + [Theory] + [InlineData(0.436075, 0.222504, 0.013932, 0.648427, 0.330856, 0.222504)] + [InlineData(0.964220, 1.000000, 0.825210, 0.345669, 0.358496, 1.000000)] + [InlineData(0.434119, 0.356820, 0.369447, 0.374116, 0.307501, 0.356820)] + [InlineData(0, 0, 0, 0.538842, 0.000000, 0.000000)] + public void Convert_Xyy_To_Xyz(float xyzX, float xyzY, float xyzZ, float x, float y, float yl) + { + CieXyy input = new(x, y, yl); + CieXyz expected = new(xyzX, xyzY, xyzZ); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0.436075, 0.222504, 0.013932, 0.648427, 0.330856, 0.222504)] + [InlineData(0.964220, 1.000000, 0.825210, 0.345669, 0.358496, 1.000000)] + [InlineData(0.434119, 0.356820, 0.369447, 0.374116, 0.307501, 0.356820)] + [InlineData(0.231809, 0, 0.077528, 0.749374, 0.000000, 0.000000)] + public void Convert_Xyz_to_Xyy(float xyzX, float xyzY, float xyzZ, float x, float y, float yl) + { + CieXyz input = new(xyzX, xyzY, xyzZ); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From b8c73e121d76caf0d3c85b495bc3110b186843f3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 4 Apr 2024 14:23:18 +1000 Subject: [PATCH 10/79] Add CMYK --- src/ImageSharp/ColorProfiles/CieXyy.cs | 10 +- src/ImageSharp/ColorProfiles/Cmyk.cs | 164 ++++++++++++++++++ ...rProfileConverterExtensionsCieLabCieLab.cs | 55 ++++++ .../ColorProfileConverterExtensionsRgbRgb.cs | 55 ++++++ .../ApproximateColorProfileComparer.cs | 7 +- .../CieLabAndCmykConversionTests.cs | 70 ++++++++ .../CmykAndYCbCrConversionTests.cs | 70 ++++++++ .../ColorProfiles/RgbAndCmykConversionTest.cs | 77 ++++++++ 8 files changed, 502 insertions(+), 6 deletions(-) create mode 100644 src/ImageSharp/ColorProfiles/Cmyk.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs diff --git a/src/ImageSharp/ColorProfiles/CieXyy.cs b/src/ImageSharp/ColorProfiles/CieXyy.cs index 9a1953ac8..4dcb582d6 100644 --- a/src/ImageSharp/ColorProfiles/CieXyy.cs +++ b/src/ImageSharp/ColorProfiles/CieXyy.cs @@ -18,7 +18,7 @@ public readonly struct CieXyy : IColorProfile /// The x chroma component. /// The y chroma component. /// The y luminance component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyy(float x, float y, float yl) { // Not clamping as documentation about this space only indicates "usual" ranges @@ -31,7 +31,7 @@ public readonly struct CieXyy : IColorProfile /// Initializes a new instance of the struct. /// /// The vector representing the x, y, Y components. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyy(Vector3 vector) : this() { @@ -67,7 +67,7 @@ public readonly struct CieXyy : IColorProfile /// /// True if the current left is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(CieXyy left, CieXyy right) => left.Equals(right); /// @@ -78,7 +78,7 @@ public readonly struct CieXyy : IColorProfile /// /// True if the current left is unequal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(CieXyy left, CieXyy right) => !left.Equals(right); /// @@ -148,7 +148,7 @@ public readonly struct CieXyy : IColorProfile public override bool Equals(object? obj) => obj is CieXyy other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieXyy other) => this.X.Equals(other.X) && this.Y.Equals(other.Y) diff --git a/src/ImageSharp/ColorProfiles/Cmyk.cs b/src/ImageSharp/ColorProfiles/Cmyk.cs new file mode 100644 index 000000000..d586ab6d4 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Cmyk.cs @@ -0,0 +1,164 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents an CMYK (cyan, magenta, yellow, keyline) color. +/// +public readonly struct Cmyk : IColorProfile +{ + private static readonly Vector4 Min = Vector4.Zero; + private static readonly Vector4 Max = Vector4.One; + + /// + /// Initializes a new instance of the struct. + /// + /// The cyan component. + /// The magenta component. + /// The yellow component. + /// The keyline black component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Cmyk(float c, float m, float y, float k) + : this(new Vector4(c, m, y, k)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the c, m, y, k components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Cmyk(Vector4 vector) + { + vector = Numerics.Clamp(vector, Min, Max); + this.C = vector.X; + this.M = vector.Y; + this.Y = vector.Z; + this.K = vector.W; + } + + /// + /// Gets the cyan color component. + /// A value ranging between 0 and 1. + /// + public readonly float C { get; } + + /// + /// Gets the magenta color component. + /// A value ranging between 0 and 1. + /// + public readonly float M { get; } + + /// + /// Gets the yellow color component. + /// A value ranging between 0 and 1. + /// + public readonly float Y { get; } + + /// + /// Gets the keyline black color component. + /// A value ranging between 0 and 1. + /// + public readonly float K { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Cmyk left, Cmyk right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Cmyk left, Cmyk right) => !left.Equals(right); + + /// + public static Cmyk FromProfileConnectingSpace(ColorConversionOptions options, in Rgb source) + { + // To CMY + Vector3 cmy = Vector3.One - source.ToScaledVector3(); + + // To CMYK + Vector3 k = new(MathF.Min(cmy.X, MathF.Min(cmy.Y, cmy.Z))); + + if (MathF.Abs(k.X - 1F) < Constants.Epsilon) + { + return new Cmyk(0, 0, 0, 1F); + } + + cmy = (cmy - k) / (Vector3.One - k); + + return new Cmyk(cmy.X, cmy.Y, cmy.Z, k.X); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + + // TODO: We can optimize this by using SIMD + for (int i = 0; i < source.Length; i++) + { + Rgb rgb = source[i]; + destination[i] = FromProfileConnectingSpace(options, in rgb); + } + } + + /// + public Rgb ToProfileConnectingSpace(ColorConversionOptions options) + { + Vector3 rgb = (Vector3.One - new Vector3(this.C, this.M, this.Y)) * (Vector3.One - new Vector3(this.K)); + return Rgb.FromScaledVector3(rgb); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + // TODO: We can possibly optimize this by using SIMD + for (int i = 0; i < source.Length; i++) + { + Cmyk cmyk = source[i]; + destination[i] = cmyk.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() + => HashCode.Combine(this.C, this.M, this.Y, this.K); + + /// + public override string ToString() + => FormattableString.Invariant($"Cmyk({this.C:#0.##}, {this.M:#0.##}, {this.Y:#0.##}, {this.K:#0.##})"); + + /// + public override bool Equals(object? obj) + => obj is Cmyk other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Cmyk other) + => this.C.Equals(other.C) + && this.M.Equals(other.M) + && this.Y.Equals(other.Y) + && this.K.Equals(other.K); +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs new file mode 100644 index 000000000..51c68531d --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs @@ -0,0 +1,55 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsCieLabCieLab +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + CieLab pcsFromA = source.ToProfileConnectingSpace(options); + CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); + + // Adapt to target white point + pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + + // Convert between PCS + CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFromTo = pcsFromToOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFromTo); + + using IMemoryOwner pcsFromOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFrom = pcsFromOwner.GetSpan(); + CieLab.ToProfileConnectionSpace(options, pcsFromTo, pcsFrom); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + + // Convert between PCS. + CieLab.FromProfileConnectionSpace(options, pcsFrom, pcsFromTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsFromTo, destination); + } +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs new file mode 100644 index 000000000..248a54026 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs @@ -0,0 +1,55 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsRgbRgb +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + Rgb pcsFromA = source.ToProfileConnectingSpace(options); + CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); + + // Adapt to target white point + pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + + // Convert between PCS + Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFromTo = pcsFromToOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFromTo); + + using IMemoryOwner pcsFromOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFrom = pcsFromOwner.GetSpan(); + Rgb.ToProfileConnectionSpace(options, pcsFromTo, pcsFrom); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + + // Convert between PCS. + Rgb.FromProfileConnectionSpace(options, pcsFrom, pcsFromTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsFromTo, destination); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs index 81aac269d..f67f47222 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -18,7 +18,8 @@ internal readonly struct ApproximateColorProfileComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer { private readonly float epsilon; @@ -46,6 +47,8 @@ internal readonly struct ApproximateColorProfileComparer : public bool Equals(CieXyy x, CieXyy y) => this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y) && this.Equals(x.Yl, y.Yl); + public bool Equals(Cmyk x, Cmyk y) => this.Equals(x.C, y.C) && this.Equals(x.M, y.M) && this.Equals(x.Y, y.Y) && this.Equals(x.K, y.K); + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); @@ -64,6 +67,8 @@ internal readonly struct ApproximateColorProfileComparer : public int GetHashCode([DisallowNull] CieXyy obj) => obj.GetHashCode(); + public int GetHashCode([DisallowNull] Cmyk obj) => obj.GetHashCode(); + private bool Equals(float x, float y) { float d = x - y; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs new file mode 100644 index 000000000..a13ad7d15 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +public class CieLabAndCmykConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 1, 0, 0, 0)] + [InlineData(0, 1, 0.6156551, 5.960464E-08, 55.063, 82.54871, 23.16506)] + public void Convert_Cmyk_to_CieLab(float c, float m, float y, float k, float l, float a, float b) + { + // Arrange + Cmyk input = new(c, m, y, k); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0, 1)] + [InlineData(36.0555, 303.6901, 10.01514, 0, 1, 0.597665966, 0)] + public void Convert_CieLab_to_Cmyk(float l, float a, float b, float c, float m, float y, float k) + { + // Arrange + CieLab input = new(l, a, b); + Cmyk expected = new(c, m, y, k); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + Cmyk actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs new file mode 100644 index 000000000..ef3305ec7 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +public class CmykAndYCbCrConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 255, 128, 128)] + [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 136.5134, 69.90555, 114.9948)] + public void Convert_Cmyk_to_YCbCr(float c, float m, float y, float k, float y2, float cb, float cr) + { + // Arrange + Cmyk input = new(c, m, y, k); + YCbCr expected = new(y2, cb, cr); + ColorProfileConverter converter = new(); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + YCbCr actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(255, 128, 128, 0, 0, 0, 5.960464E-08)] + [InlineData(136.5134, 69.90555, 114.9948, 0.2891567, 0, 0.7951807, 0.3490196)] + public void Convert_YCbCr_to_Cmyk(float y2, float cb, float cr, float c, float m, float y, float k) + { + // Arrange + YCbCr input = new(y2, cb, cr); + Cmyk expected = new(c, m, y, k); + ColorProfileConverter converter = new(); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + Cmyk actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs new file mode 100644 index 000000000..3ae2996f2 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs @@ -0,0 +1,77 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +/// +public class RgbAndCmykConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001F); + + [Theory] + [InlineData(1, 1, 1, 1, 0, 0, 0)] + [InlineData(0, 0, 0, 0, 1, 1, 1)] + [InlineData(0, 0.84, 0.037, 0.365, 0.635, 0.1016, 0.6115)] + public void Convert_Cmyk_To_Rgb(float c, float m, float y, float k, float r, float g, float b) + { + // Arrange + Cmyk input = new(c, m, y, k); + Rgb expected = new(r, g, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(1, 1, 1, 0, 0, 0, 0)] + [InlineData(0, 0, 0, 0, 0, 0, 1)] + [InlineData(0.635, 0.1016, 0.6115, 0, 0.84, 0.037, 0.365)] + public void Convert_Rgb_To_Cmyk(float r, float g, float b, float c, float m, float y, float k) + { + // Arrange + Rgb input = new(r, g, b); + Cmyk expected = new(c, m, y, k); + ColorProfileConverter converter = new(); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + Cmyk actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 55de3ccc52dfd1fd30969a852db03aecffb461c6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 4 Apr 2024 16:06:13 +1000 Subject: [PATCH 11/79] Add more LAB tests --- .../CieLabAndCieLchuvConversionTests.cs | 76 +++++++++++++++++++ .../CieLabAndCieLuvConversionTests.cs | 76 +++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs new file mode 100644 index 000000000..d7f2df95b --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs @@ -0,0 +1,76 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieLabAndCieLchuvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(30.66194, 200, 352.7564, 31.95653, 116.8745, 2.388602)] + public void Convert_Lchuv_to_Lab(float l, float c, float h, float l2, float a, float b) + { + // Arrange + CieLchuv input = new(l, c, h); + CieLab expected = new(l2, a, b); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 303.6901, 10.01514, 29.4713573, 200, 352.6346)] + public void Convert_Lab_to_Lchuv(float l, float a, float b, float l2, float c, float h) + { + // Arrange + CieLab input = new(l, a, b); + CieLchuv expected = new(l2, c, h); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; + + // Act + CieLchuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs new file mode 100644 index 000000000..17df78bdf --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs @@ -0,0 +1,76 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieLabAndCieLuvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(10, 36.0555, 303.6901, 10.6006718, -17.24077, 82.8835)] + public void Convert_CieLuv_to_CieLab(float l, float u, float v, float l2, float a, float b) + { + // Arrange + CieLuv input = new(l, u, v); + CieLab expected = new(l2, a, b); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(10.0151367, -23.9644356, 17.0226, 10.0000038, -12.830183, 15.1829338)] + public void Convert_CieLab_to_CieLuv(float l, float a, float b, float l2, float u, float v) + { + // Arrange + CieLab input = new(l, a, b); + CieLuv expected = new(l2, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 51c3094799041e53478e0a6fbbfc7f9575873c89 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 4 Apr 2024 16:52:01 +1000 Subject: [PATCH 12/79] More LAB tests --- .../CieLabAndCieLchuvConversionTests.cs | 4 +- .../CieLabAndCieLuvConversionTests.cs | 4 +- .../CieLabAndCieXyyConversionTests.cs | 70 +++++++++++++++++++ 3 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs index d7f2df95b..7bf7b4231 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs @@ -19,7 +19,7 @@ public class CieLabAndCieLchuvConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(30.66194, 200, 352.7564, 31.95653, 116.8745, 2.388602)] - public void Convert_Lchuv_to_Lab(float l, float c, float h, float l2, float a, float b) + public void Convert_Lchuv_To_Lab(float l, float c, float h, float l2, float a, float b) { // Arrange CieLchuv input = new(l, c, h); @@ -48,7 +48,7 @@ public class CieLabAndCieLchuvConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(36.0555, 303.6901, 10.01514, 29.4713573, 200, 352.6346)] - public void Convert_Lab_to_Lchuv(float l, float a, float b, float l2, float c, float h) + public void Convert_Lab_To_Lchuv(float l, float a, float b, float l2, float c, float h) { // Arrange CieLab input = new(l, a, b); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs index 17df78bdf..fb9f25bd8 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs @@ -19,7 +19,7 @@ public class CieLabAndCieLuvConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(10, 36.0555, 303.6901, 10.6006718, -17.24077, 82.8835)] - public void Convert_CieLuv_to_CieLab(float l, float u, float v, float l2, float a, float b) + public void Convert_CieLuv_To_CieLab(float l, float u, float v, float l2, float a, float b) { // Arrange CieLuv input = new(l, u, v); @@ -48,7 +48,7 @@ public class CieLabAndCieLuvConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(10.0151367, -23.9644356, 17.0226, 10.0000038, -12.830183, 15.1829338)] - public void Convert_CieLab_to_CieLuv(float l, float a, float b, float l2, float u, float v) + public void Convert_CieLab_To_CieLuv(float l, float a, float b, float l2, float u, float v) { // Arrange CieLab input = new(l, a, b); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs new file mode 100644 index 000000000..074173da5 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +public class CieLabAndCieXyyConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.8644734, 0.06098868, 0.06509002, 30.6619, 291.5721, -11.2526)] + public void Convert_CieXyy_To_CieLab(float x, float y, float yl, float l, float a, float b) + { + // Arrange + CieXyy input = new(x, y, yl); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(30.6619, 291.5721, -11.2526, 0.8644734, 0.06098868, 0.06509002)] + public void Convert_CieLab_To_CieXyy(float l, float a, float b, float x, float y, float yl) + { + // Arrange + CieLab input = new(l, a, b); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 611c78dcd7bf4d83bbcb4bc0bdcd9711538c4e25 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 4 Apr 2024 21:12:05 +1000 Subject: [PATCH 13/79] More tests --- .../CieLabAndRgbConversionTests.cs | 71 +++++++++++++++++++ .../CieLchuvAndCieLchConversionTests.cs | 71 +++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs new file mode 100644 index 000000000..3b0b2f96a --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +public class CieLabAndRgbConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + private static readonly ColorProfileConverter Converter = new(); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.9999999, 0, 0.384345, 55.063, 82.54871, 23.16505)] + public void Convert_Rgb_to_CieLab(float r, float g, float b2, float l, float a, float b) + { + // Arrange + Rgb input = new(r, g, b2); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(55.063, 82.54871, 23.16505, 0.9999999, 0, 0.384345)] + public void Convert_CieLab_to_Rgb(float l, float a, float b, float r, float g, float b2) + { + // Arrange + CieLab input = new(l, a, b); + Rgb expected = new(r, g, b2); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs new file mode 100644 index 000000000..580e66e5e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +public class CieLchuvAndCieLchConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.73742, 64.79149, 30.1786, 36.0555, 103.6901, 10.01513)] + public void Convert_CieLch_To_CieLchuv(float l2, float c2, float h2, float l, float c, float h) + { + // Arrange + CieLch input = new(l2, c2, h2); + CieLchuv expected = new(l, c, h); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; + + // Act + CieLchuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(36.0555, 103.6901, 10.01514, 36.73742, 64.79149, 30.1786)] + public void Convert_CieLchuv_To_CieLch(float l, float c, float h, float l2, float c2, float h2) + { + // Arrange + CieLchuv input = new(l, c, h); + CieLch expected = new(l2, c2, h2); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + CieLch actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 27b60846f49d840eb0468f24ab4a394850637c8d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 4 Apr 2024 21:20:51 +1000 Subject: [PATCH 14/79] LAB YCBCR tests --- .../CieLabAndYCbCrConversionTests.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs new file mode 100644 index 000000000..6f4d7ec42 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +public class CieLabAndYCbCrConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 128, 128, 0, 0, 0)] + [InlineData(87.4179, 133.9763, 247.5308, 55.06287, 82.54838, 23.1697)] + public void Convert_YCbCr_to_CieLab(float y, float cb, float cr, float l, float a, float b) + { + // Arrange + YCbCr input = new(y, cb, cr); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(55.06287, 82.54838, 23.1697, 87.41701, 133.97232, 247.5314)] + public void Convert_CieLab_to_YCbCr(float l, float a, float b, float y, float cb, float cr) + { + // Arrange + CieLab input = new(l, a, b); + YCbCr expected = new(y, cb, cr); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + YCbCr actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 05b6cfb335bc4cd35dfeb29871c76d966ff5635e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 17 Apr 2024 22:53:08 +1000 Subject: [PATCH 15/79] Create Hsl.cs --- src/ImageSharp/ColorProfiles/Hsl.cs | 243 ++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 src/ImageSharp/ColorProfiles/Hsl.cs diff --git a/src/ImageSharp/ColorProfiles/Hsl.cs b/src/ImageSharp/ColorProfiles/Hsl.cs new file mode 100644 index 000000000..dd8772d96 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Hsl.cs @@ -0,0 +1,243 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents a Hsl (hue, saturation, lightness) color. +/// +public readonly struct Hsl : IColorProfile +{ + private static readonly Vector3 Min = Vector3.Zero; + private static readonly Vector3 Max = new(360, 1, 1); + + /// + /// Initializes a new instance of the struct. + /// + /// The h hue component. + /// The s saturation component. + /// The l value (lightness) component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Hsl(float h, float s, float l) + : this(new Vector3(h, s, l)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the h, s, l components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Hsl(Vector3 vector) + { + vector = Vector3.Clamp(vector, Min, Max); + this.H = vector.X; + this.S = vector.Y; + this.L = vector.Z; + } + + /// + /// Gets the hue component. + /// A value ranging between 0 and 360. + /// + public readonly float H { get; } + + /// + /// Gets the saturation component. + /// A value ranging between 0 and 1. + /// + public readonly float S { get; } + + /// + /// Gets the lightness component. + /// A value ranging between 0 and 1. + /// + public readonly float L { get; } + + /// + /// Compares two objects for equality. + /// + /// + /// The on the left side of the operand. + /// + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Hsl left, Hsl right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Hsl left, Hsl right) => !left.Equals(right); + + /// + public static Hsl FromProfileConnectingSpace(ColorConversionOptions options, in Rgb source) + { + float r = source.R; + float g = source.G; + float b = source.B; + + float max = MathF.Max(r, MathF.Max(g, b)); + float min = MathF.Min(r, MathF.Min(g, b)); + float chroma = max - min; + float h = 0F; + float s = 0F; + float l = (max + min) / 2F; + + if (MathF.Abs(chroma) < Constants.Epsilon) + { + return new Hsl(0F, s, l); + } + + if (MathF.Abs(r - max) < Constants.Epsilon) + { + h = (g - b) / chroma; + } + else if (MathF.Abs(g - max) < Constants.Epsilon) + { + h = 2F + ((b - r) / chroma); + } + else if (MathF.Abs(b - max) < Constants.Epsilon) + { + h = 4F + ((r - g) / chroma); + } + + h *= 60F; + if (h < 0F) + { + h += 360F; + } + + if (l <= .5F) + { + s = chroma / (max + min); + } + else + { + s = chroma / (2F - max - min); + } + + return new Hsl(h, s, l); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + Rgb rgb = source[i]; + destination[i] = FromProfileConnectingSpace(options, in rgb); + } + } + + /// + public Rgb ToProfileConnectingSpace(ColorConversionOptions options) + { + float rangedH = this.H / 360F; + float r = 0; + float g = 0; + float b = 0; + float s = this.S; + float l = this.L; + + if (MathF.Abs(l) > Constants.Epsilon) + { + if (MathF.Abs(s) < Constants.Epsilon) + { + r = g = b = l; + } + else + { + float temp2 = (l < .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 Rgb(r, g, b); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + Hsl hsl = source[i]; + destination[i] = hsl.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.L); + + /// + public override string ToString() => FormattableString.Invariant($"Hsl({this.H:#0.##}, {this.S:#0.##}, {this.L:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is Hsl other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Hsl other) + => this.H.Equals(other.H) + && this.S.Equals(other.S) + && this.L.Equals(other.L); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float GetColorComponent(float first, float second, float third) + { + third = MoveIntoRange(third); + if (third < 0.1666667F) + { + return first + ((second - first) * 6F * third); + } + + if (third < .5F) + { + return second; + } + + if (third < 0.6666667F) + { + return first + ((second - first) * (0.6666667F - third) * 6F); + } + + return first; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float MoveIntoRange(float value) + { + if (value < 0F) + { + value++; + } + else if (value > 1F) + { + value--; + } + + return value; + } +} From bb6502ca6a6bcde37d12bd5defee2cddf21f8055 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 17 Apr 2024 22:57:00 +1000 Subject: [PATCH 16/79] Create Hsv.cs --- src/ImageSharp/ColorProfiles/Hsv.cs | 229 ++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 src/ImageSharp/ColorProfiles/Hsv.cs diff --git a/src/ImageSharp/ColorProfiles/Hsv.cs b/src/ImageSharp/ColorProfiles/Hsv.cs new file mode 100644 index 000000000..6a7e51332 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/Hsv.cs @@ -0,0 +1,229 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness). +/// +public readonly struct Hsv : IColorProfile +{ + private static readonly Vector3 Min = Vector3.Zero; + private static readonly Vector3 Max = new(360, 1, 1); + + /// + /// Initializes a new instance of the struct. + /// + /// The h hue component. + /// The s saturation component. + /// The v value (brightness) component. + [MethodImpl(InliningOptions.ShortMethod)] + public Hsv(float h, float s, float v) + : this(new Vector3(h, s, v)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the h, s, v components. + [MethodImpl(InliningOptions.ShortMethod)] + public Hsv(Vector3 vector) + { + vector = Vector3.Clamp(vector, Min, Max); + this.H = vector.X; + this.S = vector.Y; + this.V = vector.Z; + } + + /// + /// Gets the hue component. + /// A value ranging between 0 and 360. + /// + public readonly float H { get; } + + /// + /// Gets the saturation component. + /// A value ranging between 0 and 1. + /// + public readonly float S { get; } + + /// + /// Gets the value (brightness) component. + /// A value ranging between 0 and 1. + /// + public readonly float V { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator ==(Hsv left, Hsv right) => left.Equals(right); + + /// + /// Compares two objects for inequality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(InliningOptions.ShortMethod)] + public static bool operator !=(Hsv left, Hsv right) => !left.Equals(right); + + /// + public static Hsv FromProfileConnectingSpace(ColorConversionOptions options, in Rgb source) + { + float r = source.R; + float g = source.G; + float b = source.B; + + float max = MathF.Max(r, MathF.Max(g, b)); + float min = MathF.Min(r, MathF.Min(g, b)); + float chroma = max - min; + float h = 0; + float s = 0; + float v = max; + + if (MathF.Abs(chroma) < Constants.Epsilon) + { + return new Hsv(0, s, v); + } + + if (MathF.Abs(r - max) < Constants.Epsilon) + { + h = (g - b) / chroma; + } + else if (MathF.Abs(g - max) < Constants.Epsilon) + { + h = 2 + ((b - r) / chroma); + } + else if (MathF.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); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + Rgb rgb = source[i]; + destination[i] = FromProfileConnectingSpace(options, in rgb); + } + } + + /// + public Rgb ToProfileConnectingSpace(ColorConversionOptions options) + { + float s = this.S; + float v = this.V; + + if (MathF.Abs(s) < Constants.Epsilon) + { + return new Rgb(v, v, v); + } + + float h = (MathF.Abs(this.H - 360) < Constants.Epsilon) ? 0 : this.H / 60; + int i = (int)Math.Truncate(h); + float f = h - i; + + float p = v * (1F - s); + float q = v * (1F - (s * f)); + float t = v * (1F - (s * (1F - 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 Rgb(r, g, b); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + Hsv hsv = source[i]; + destination[i] = hsv.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.V); + + /// + public override string ToString() => FormattableString.Invariant($"Hsv({this.H:#0.##}, {this.S:#0.##}, {this.V:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is Hsv other && this.Equals(other); + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public bool Equals(Hsv other) + => this.H.Equals(other.H) + && this.S.Equals(other.S) + && this.V.Equals(other.V); +} From 698d76e8360b3fe821ef734ddb2f1fbc03dba1c1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 17 Apr 2024 23:07:12 +1000 Subject: [PATCH 17/79] Create HunterLab.cs --- src/ImageSharp/ColorProfiles/HunterLab.cs | 202 ++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 src/ImageSharp/ColorProfiles/HunterLab.cs diff --git a/src/ImageSharp/ColorProfiles/HunterLab.cs b/src/ImageSharp/ColorProfiles/HunterLab.cs new file mode 100644 index 000000000..cf6dbd363 --- /dev/null +++ b/src/ImageSharp/ColorProfiles/HunterLab.cs @@ -0,0 +1,202 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp.ColorProfiles; + +/// +/// Represents an Hunter LAB color. +/// . +/// +public readonly struct HunterLab : IColorProfile +{ + /// + /// Initializes a new instance of the struct. + /// + /// The lightness dimension. + /// The a (green - magenta) component. + /// The b (blue - yellow) component. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HunterLab(float l, float a, float b) + : this(new Vector3(l, a, b)) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l a b components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HunterLab(Vector3 vector) + { + // Not clamping as documentation about this space only indicates "usual" ranges + this.L = vector.X; + this.A = vector.Y; + this.B = vector.Z; + } + + /// + /// Gets the lightness dimension. + /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white). + /// + public readonly float L { get; } + + /// + /// Gets the a color component. + /// A value usually ranging from -100 to 100. Negative is green, positive magenta. + /// + public readonly float A { get; } + + /// + /// Gets the b color component. + /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow + /// + public readonly float B { get; } + + /// + /// Gets the reference white point of this color. + /// + public readonly CieXyz WhitePoint { get; } + + /// + /// Compares two objects for equality. + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is equal to the parameter; otherwise, false. + /// + public static bool operator ==(HunterLab left, HunterLab right) => left.Equals(right); + + /// + /// Compares two objects for inequality + /// + /// The on the left side of the operand. + /// The on the right side of the operand. + /// + /// True if the current left is unequal to the parameter; otherwise, false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(HunterLab left, HunterLab right) => !left.Equals(right); + + /// + public static HunterLab FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) + { + // Conversion algorithm described here: + // http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab + CieXyz whitePoint = options.TargetWhitePoint; + float x = source.X, y = source.Y, z = source.Z; + float xn = whitePoint.X, yn = whitePoint.Y, zn = whitePoint.Z; + + float ka = ComputeKa(in whitePoint); + float kb = ComputeKb(in whitePoint); + + float yByYn = y / yn; + float sqrtYbyYn = MathF.Sqrt(yByYn); + float l = 100 * sqrtYbyYn; + float a = ka * (((x / xn) - yByYn) / sqrtYbyYn); + float b = kb * ((yByYn - (z / zn)) / sqrtYbyYn); + + if (float.IsNaN(a)) + { + a = 0; + } + + if (float.IsNaN(b)) + { + b = 0; + } + + return new HunterLab(l, a, b); + } + + /// + public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span 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); + } + } + + /// + public CieXyz ToProfileConnectingSpace(ColorConversionOptions options) + { + // Conversion algorithm described here: + // http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab + CieXyz whitePoint = options.WhitePoint; + float l = this.L, a = this.A, b = this.B; + float xn = whitePoint.X, yn = whitePoint.Y, zn = whitePoint.Z; + + float ka = ComputeKa(whitePoint); + float kb = ComputeKb(whitePoint); + + float pow = Numerics.Pow2(l / 100F); + float sqrtPow = MathF.Sqrt(pow); + float y = pow * yn; + + float x = (((a / ka) * sqrtPow) + pow) * xn; + float z = (((b / kb) * sqrtPow) - pow) * (-zn); + + return new CieXyz(x, y, z); + } + + /// + public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan source, Span destination) + { + Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); + for (int i = 0; i < source.Length; i++) + { + HunterLab lab = source[i]; + destination[i] = lab.ToProfileConnectingSpace(options); + } + } + + /// + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B, this.WhitePoint); + + /// + public override string ToString() => FormattableString.Invariant($"HunterLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is HunterLab other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(HunterLab other) + => this.L.Equals(other.L) + && this.A.Equals(other.A) + && this.B.Equals(other.B) + && this.WhitePoint.Equals(other.WhitePoint); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float ComputeKa(in CieXyz whitePoint) + { + if (whitePoint.Equals(Illuminants.C)) + { + return 175F; + } + + return 100F * (175F / 198.04F) * (whitePoint.X + whitePoint.Y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float ComputeKb(in CieXyz whitePoint) + { + if (whitePoint == Illuminants.C) + { + return 70F; + } + + return 100F * (70F / 218.11F) * (whitePoint.Y + whitePoint.Z); + } +} From e7a2445dba6a89d97dcc8e0291853ee792da4259 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 17 Apr 2024 23:07:19 +1000 Subject: [PATCH 18/79] Cleanup --- src/ImageSharp/ColorProfiles/CieLab.cs | 6 ++++-- src/ImageSharp/ColorProfiles/Hsv.cs | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs index 3578e2713..5cda362d4 100644 --- a/src/ImageSharp/ColorProfiles/CieLab.cs +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -104,7 +104,8 @@ public readonly struct CieLab : IProfileConnectingSpace [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 + // 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; @@ -174,5 +175,6 @@ public readonly struct CieLab : IProfileConnectingSpace } /// - public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.WhitePoint; } diff --git a/src/ImageSharp/ColorProfiles/Hsv.cs b/src/ImageSharp/ColorProfiles/Hsv.cs index 6a7e51332..cbcdd4a1b 100644 --- a/src/ImageSharp/ColorProfiles/Hsv.cs +++ b/src/ImageSharp/ColorProfiles/Hsv.cs @@ -20,7 +20,7 @@ public readonly struct Hsv : IColorProfile /// The h hue component. /// The s saturation component. /// The v value (brightness) component. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Hsv(float h, float s, float v) : this(new Vector3(h, s, v)) { @@ -30,7 +30,7 @@ public readonly struct Hsv : IColorProfile /// Initializes a new instance of the struct. /// /// The vector representing the h, s, v components. - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Hsv(Vector3 vector) { vector = Vector3.Clamp(vector, Min, Max); @@ -65,7 +65,7 @@ public readonly struct Hsv : IColorProfile /// /// True if the current left is equal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Hsv left, Hsv right) => left.Equals(right); /// @@ -76,7 +76,7 @@ public readonly struct Hsv : IColorProfile /// /// True if the current left is unequal to the parameter; otherwise, false. /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Hsv left, Hsv right) => !left.Equals(right); /// @@ -211,7 +211,7 @@ public readonly struct Hsv : IColorProfile => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.V); /// @@ -221,7 +221,7 @@ public readonly struct Hsv : IColorProfile public override bool Equals(object? obj) => obj is Hsv other && this.Equals(other); /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Hsv other) => this.H.Equals(other.H) && this.S.Equals(other.S) From 2fbabe828cf0d1ea67b5071312c05a4e0ec27ac4 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 18 Apr 2024 21:48:41 +1000 Subject: [PATCH 19/79] Complete HunterLab and add more tests --- src/ImageSharp/ColorProfiles/HunterLab.cs | 4 +- .../ApproximateColorProfileComparer.cs | 17 ++++- .../CieLabAndHslConversionTests.cs | 70 +++++++++++++++++++ .../CieLabAndHsvConversionTests.cs | 70 +++++++++++++++++++ .../CieLabAndHunterLabConversionTests.cs | 70 +++++++++++++++++++ 5 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndHslConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndHsvConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndHunterLabConversionTests.cs diff --git a/src/ImageSharp/ColorProfiles/HunterLab.cs b/src/ImageSharp/ColorProfiles/HunterLab.cs index cf6dbd363..97a9004f7 100644 --- a/src/ImageSharp/ColorProfiles/HunterLab.cs +++ b/src/ImageSharp/ColorProfiles/HunterLab.cs @@ -132,8 +132,8 @@ public readonly struct HunterLab : IColorProfile float l = this.L, a = this.A, b = this.B; float xn = whitePoint.X, yn = whitePoint.Y, zn = whitePoint.Z; - float ka = ComputeKa(whitePoint); - float kb = ComputeKb(whitePoint); + float ka = ComputeKa(in whitePoint); + float kb = ComputeKb(in whitePoint); float pow = Numerics.Pow2(l / 100F); float sqrtPow = MathF.Sqrt(pow); diff --git a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs index f67f47222..56d495a87 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ApproximateColorProfileComparer.cs @@ -19,7 +19,10 @@ internal readonly struct ApproximateColorProfileComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, - IEqualityComparer + IEqualityComparer, + IEqualityComparer, + IEqualityComparer, + IEqualityComparer { private readonly float epsilon; @@ -49,6 +52,12 @@ internal readonly struct ApproximateColorProfileComparer : public bool Equals(Cmyk x, Cmyk y) => this.Equals(x.C, y.C) && this.Equals(x.M, y.M) && this.Equals(x.Y, y.Y) && this.Equals(x.K, y.K); + public bool Equals(Hsl x, Hsl y) => this.Equals(x.H, y.H) && this.Equals(x.S, y.S) && this.Equals(x.L, y.L); + + public bool Equals(Hsv x, Hsv y) => this.Equals(x.H, y.H) && this.Equals(x.S, y.S) && this.Equals(x.V, y.V); + + public bool Equals(HunterLab x, HunterLab y) => this.Equals(x.L, y.L) && this.Equals(x.A, y.A) && this.Equals(x.B, y.B); + public int GetHashCode([DisallowNull] CieLab obj) => obj.GetHashCode(); public int GetHashCode([DisallowNull] CieXyz obj) => obj.GetHashCode(); @@ -69,6 +78,12 @@ internal readonly struct ApproximateColorProfileComparer : public int GetHashCode([DisallowNull] Cmyk obj) => obj.GetHashCode(); + public int GetHashCode([DisallowNull] Hsl obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] Hsv obj) => obj.GetHashCode(); + + public int GetHashCode([DisallowNull] HunterLab obj) => obj.GetHashCode(); + private bool Equals(float x, float y) { float d = x - y; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHslConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHslConversionTests.cs new file mode 100644 index 000000000..96779f189 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHslConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLabAndHslConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(336.9393, 1, 0.5, 55.063, 82.54868, 23.16508)] + public void Convert_Hsl_to_CieLab(float h, float s, float ll, float l, float a, float b) + { + // Arrange + Hsl input = new(h, s, ll); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(55.063, 82.54868, 23.16508, 336.9393, 1, 0.5)] + public void Convert_CieLab_to_Hsl(float l, float a, float b, float h, float s, float ll) + { + // Arrange + CieLab input = new(l, a, b); + Hsl expected = new(h, s, ll); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + Hsl actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHsvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHsvConversionTests.cs new file mode 100644 index 000000000..3389da981 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHsvConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLabAndHsvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(336.9393, 1, 0.9999999, 55.063, 82.54871, 23.16504)] + public void Convert_Hsv_to_CieLab(float h, float s, float v, float l, float a, float b) + { + // Arrange + Hsv input = new(h, s, v); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(55.063, 82.54871, 23.16504, 336.9393, 1, 0.9999999)] + public void Convert_CieLab_to_Hsv(float l, float a, float b, float h, float s, float v) + { + // Arrange + CieLab input = new(l, a, b); + Hsv expected = new(h, s, v); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + Hsv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHunterLabConversionTests.cs new file mode 100644 index 000000000..1054d537a --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndHunterLabConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLabAndHunterLabConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(27.51646, 556.9392, -0.03974226, 33.074177, 281.48329, -0.06948)] + public void Convert_HunterLab_to_CieLab(float l2, float a2, float b2, float l, float a, float b) + { + // Arrange + HunterLab input = new(l2, a2, b2); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(33.074177, 281.48329, -0.06948, 27.51646, 556.9392, -0.03974226)] + public void Convert_CieLab_to_HunterLab(float l, float a, float b, float l2, float a2, float b2) + { + // Arrange + CieLab input = new(l, a, b); + HunterLab expected = new(l2, a2, b2); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new HunterLab[5]; + + // Act + HunterLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From f37ac8e7044c015c19ecbfe99f9dd23a4a65a213 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 24 Apr 2024 22:07:17 +1000 Subject: [PATCH 20/79] Add LMS and Lch tests --- src/ImageSharp/ColorProfiles/CieLch.cs | 9 +-- .../CieLabAndLmsConversionTests.cs | 70 ++++++++++++++++++ .../CieLchAndCieLuvConversionTests.cs | 71 +++++++++++++++++++ 3 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabAndLmsConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs diff --git a/src/ImageSharp/ColorProfiles/CieLch.cs b/src/ImageSharp/ColorProfiles/CieLch.cs index 23f250d0c..a8d70cfb7 100644 --- a/src/ImageSharp/ColorProfiles/CieLch.cs +++ b/src/ImageSharp/ColorProfiles/CieLch.cs @@ -12,12 +12,6 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// public readonly struct CieLch : IColorProfile { - /// - /// D50 standard illuminant. - /// Used when reference white is not specified explicitly. - /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; - private static readonly Vector3 Min = new(0, -200, 0); private static readonly Vector3 Max = new(100, 200, 360); @@ -165,5 +159,6 @@ public readonly struct CieLch : IColorProfile } /// - public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; + public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() + => ChromaticAdaptionWhitePointSource.WhitePoint; } diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndLmsConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndLmsConversionTests.cs new file mode 100644 index 000000000..77243268f --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndLmsConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLabAndLmsConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.8303261, -0.5776886, 0.1133359, 30.66193, 291.57209, -11.25262)] + public void Convert_Lms_to_CieLab(float l2, float m, float s, float l, float a, float b) + { + // Arrange + Lms input = new(l2, m, s); + CieLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Lms[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLab[5]; + + // Act + CieLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(30.66193, 291.57209, -11.25262, 0.8303261, -0.5776886, 0.1133359)] + public void Convert_CieLab_to_Lms(float l, float a, float b, float l2, float m, float s) + { + // Arrange + CieLab input = new(l, a, b); + Lms expected = new(l2, m, s); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new Lms[5]; + + // Act + Lms actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs new file mode 100644 index 000000000..cb0e587be --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs @@ -0,0 +1,71 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLchAndCieLuvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 34.89777, 187.6642, -7.181467)] + public void Convert_CieLch_to_CieLuv(float l, float c, float h, float l2, float u, float v) + { + // Arrange + CieLch input = new(l, c, h); + CieLuv expected = new(l2, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(34.89777, 187.6642, -7.181467, 36.0555, 103.6901, 10.01514)] + public void Convert_CieLuv_to_CieLch(float l2, float u, float v, float l, float c, float h) + { + // Arrange + CieLuv input = new(l2, u, v); + CieLch expected = new(l, c, h); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + CieLch actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 00162b640d59101cf759c7e93f734073a3607fa5 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 24 Apr 2024 22:34:29 +1000 Subject: [PATCH 21/79] Create CieLchAndCieXyyConversionTests.cs --- .../CieLchAndCieXyyConversionTests.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieXyyConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieXyyConversionTests.cs new file mode 100644 index 000000000..94f5515bf --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieXyyConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLchAndCieXyyConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0003f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 0.67641, 0.22770, 0.09037)] + public void Convert_CieLch_to_CieXyy(float l, float c, float h, float x, float y, float yl) + { + // Arrange + CieLch input = new(l, c, h); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.67641, 0.22770, 0.09037, 36.05544, 103.691315, 10.012783)] + public void Convert_CieXyy_to_CieLch(float x, float y, float yl, float l, float c, float h) + { + // Arrange + CieXyy input = new(x, y, yl); + CieLch expected = new(l, c, h); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + CieLch actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From ec14dc266f0ea121c4ee0624a1e8f4dda3d2961d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 29 Apr 2024 21:54:00 +1000 Subject: [PATCH 22/79] Create CieLchuvAndCmykConversionTests.cs --- .../CieLchuvAndCmykConversionTests.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs new file mode 100644 index 000000000..2fb97871e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLchuvAndCmykConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001F); + + [Theory] + [InlineData(0, 0, 0, 1, 0, 0, 0)] + [InlineData(0, 0.8576171, 0.7693201, 0.3440427, 36.0555, 103.6901, 10.01514)] + public void Convert_Cmyk_to_CieLchuv(float c2, float m, float y, float k, float l, float c, float h) + { + // Arrange + Cmyk input = new(c2, m, y, k); + CieLchuv expected = new(l, c, h); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; + + // Act + CieLchuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0, 1)] + [InlineData(36.0555, 103.6901, 10.01514, 0, 0.8576171, 0.7693201, 0.3440427)] + public void Convert_CieLchuv_to_Cmyk(float l, float c, float h, float c2, float m, float y, float k) + { + // Arrange + CieLchuv input = new(l, c, h); + Cmyk expected = new(c2, m, y, k); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + Cmyk actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 4efb1211c0cc1d5ec84cb404e67c99ae32107e45 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 29 Apr 2024 22:01:58 +1000 Subject: [PATCH 23/79] Create CieLuvAndCieXyyConversionTests.cs --- .../CieLuvAndCieXyyConversionTests.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs new file mode 100644 index 000000000..edc342eaa --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLuvAndCieXyyConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 103.6901, 10.01514, 0.5646762, 0.2932749, 0.09037033)] + public void Convert_CieLuv_to_CieXyy(float l, float u, float v, float x, float y, float yl) + { + // Arrange + CieLuv input = new(l, u, v); + CieXyy expected = new(x, y, yl); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.5646762, 0.2932749, 0.09037033, 36.0555, 103.6901, 10.01514)] + public void Convert_CieXyy_to_CieLuv(float x, float y, float yl, float l, float u, float v) + { + // Arrange + CieXyy input = new(x, y, yl); + CieLuv expected = new(l, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From f3f1d5ac435ce44676d1eef7503900aff326f990 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 1 May 2024 22:14:37 +1000 Subject: [PATCH 24/79] Create CieLuvAndHslConversionTests.cs --- .../CieLuvAndHslConversionTests.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs new file mode 100644 index 000000000..c6fb40149 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLuvAndHslConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 347.3767, 0.7115612, 0.3765343)] + public void Convert_CieLuv_to_Hsl(float l, float u, float v, float h, float s, float l2) + { + // Arrange + CieLuv input = new(l, u, v); + Hsl expected = new(h, s, l2); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + Hsl actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(347.3767, 0.7115612, 0.3765343, 36.0555, 93.69012, 10.01514)] + public void Convert_Hsl_to_CieLuv(float h, float s, float l2, float l, float u, float v) + { + // Arrange + Hsl input = new(h, s, l2); + CieLuv expected = new(l, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From cd6052eca91488a307bc09ee129818438fda7ef7 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 1 May 2024 22:18:34 +1000 Subject: [PATCH 25/79] Create CieLuvAndHsvConversionTests.cs --- .../CieLuvAndHsvConversionTests.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs new file mode 100644 index 000000000..f736b9a28 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLuvAndHsvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 347.3767, 0.8314762, 0.6444615)] + public void Convert_CieLuv_to_Hsv(float l, float u, float v, float h, float s, float v2) + { + // Arrange + CieLuv input = new(l, u, v); + Hsv expected = new(h, s, v2); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + Hsv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(347.3767, 0.8314762, 0.6444615, 36.0555, 93.69012, 10.01514)] + public void Convert_Hsv_to_CieLuv(float h, float s, float v2, float l, float u, float v) + { + // Arrange + Hsv input = new(h, s, v2); + CieLuv expected = new(l, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From b983cd3d885ab60eefedff9c5f7d598d4e4cabcc Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 1 May 2024 22:26:25 +1000 Subject: [PATCH 26/79] Create CieLuvAndHunterLabConversionTests.cs --- .../CieLuvAndHunterLabConversionTests.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs new file mode 100644 index 000000000..6f674f53e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLuvAndHunterLabConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 30.59289, 48.55542, 9.80487)] + public void Convert_CieLuv_to_HunterLab(float l, float u, float v, float l2, float a, float b) + { + // Arrange + CieLuv input = new(l, u, v); + HunterLab expected = new(l2, a, b); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new HunterLab[5]; + + // Act + HunterLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(30.59289, 48.55542, 9.80487, 36.0555, 93.6901, 10.01514)] + public void Convert_HunterLab_to_CieLuv(float l2, float a, float b, float l, float u, float v) + { + // Arrange + HunterLab input = new(l2, a, b); + CieLuv expected = new(l, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 9d1cb16cbd3f9d2f46abe08e1e6f2497eaff5ef5 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 7 May 2024 22:14:19 +1000 Subject: [PATCH 27/79] Add CIeLuv tests --- .../CieLuvAndLmsConversionTests.cs | 72 +++++++++++++++++++ .../CieLuvAndRgbConversionTests.cs | 72 +++++++++++++++++++ .../CieLuvAndYCbCrConversionTests.cs | 72 +++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs new file mode 100644 index 000000000..f2f5ada62 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLuvAndLmsConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 0.164352, 0.03267485, 0.0483408)] + public void Convert_CieLuv_to_Lms(float l, float u, float v, float l2, float m, float s) + { + // Arrange + CieLuv input = new(l, u, v); + Lms expected = new(l2, m, s); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Lms[5]; + + // Act + Lms actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.164352, 0.03267485, 0.0483408, 36.0555, 93.69009, 10.01514)] + public void Convert_Lms_to_CieLuv(float l2, float m, float s, float l, float u, float v) + { + // Arrange + Lms input = new(l2, m, s); + CieLuv expected = new(l, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new Lms[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs new file mode 100644 index 000000000..b0106fc57 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLuvAndRgbConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(36.0555, 93.6901, 10.01514, 0.6444615, 0.1086071, 0.2213444)] + public void Convert_CieLuv_to_Rgb(float l, float u, float v, float r, float g, float b) + { + // Arrange + CieLuv input = new(l, u, v); + Rgb expected = new(r, g, b); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.6444615, 0.1086071, 0.2213444, 36.0555, 93.69012, 10.01514)] + public void Convert_Rgb_to_CieLuv(float r, float g, float b, float l, float u, float v) + { + // Arrange + Rgb input = new(r, g, b); + CieLuv expected = new(l, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs new file mode 100644 index 000000000..6fdb440d7 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs @@ -0,0 +1,72 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieLuvAndYCbCrConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(36.0555, 93.6901, 10.01514, 71.8283, 119.3174, 193.9839)] + public void Convert_CieLuv_to_YCbCr(float l, float u, float v, float y, float cb, float cr) + { + // Arrange + CieLuv input = new(l, u, v); + YCbCr expected = new(y, cb, cr); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + YCbCr actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 128, 128, 0, 0, 0)] + [InlineData(71.8283, 119.3174, 193.9839, 36.00565, 93.44593, 10.2234)] + public void Convert_YCbCr_to_CieLuv(float y, float cb, float cr, float l, float u, float v) + { + // Arrange + YCbCr input = new(y, cb, cr); + CieLuv expected = new(l, u, v); + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From cc49c7a9d76ad2b72f00e2cd0043df7757b44d72 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 7 May 2024 22:55:15 +1000 Subject: [PATCH 28/79] Add Xyy and HSV/HSL tests --- src/ImageSharp/ColorProfiles/Hsv.cs | 2 +- .../CieXyyAndHslConversionTests.cs | 70 +++++++++++++++++++ .../CieXyyAndHsvConversionTests.cs | 70 +++++++++++++++++++ 3 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHslConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs diff --git a/src/ImageSharp/ColorProfiles/Hsv.cs b/src/ImageSharp/ColorProfiles/Hsv.cs index cbcdd4a1b..787b8ad41 100644 --- a/src/ImageSharp/ColorProfiles/Hsv.cs +++ b/src/ImageSharp/ColorProfiles/Hsv.cs @@ -112,7 +112,7 @@ public readonly struct Hsv : IColorProfile } h *= 60; - if (h < 0.0) + if (h < 0f) { h += 360; } diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHslConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHslConversionTests.cs new file mode 100644 index 000000000..3e93206ce --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHslConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyyAndHslConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.2138507)] + public void Convert_CieXyy_to_Hsl(float x, float y, float yl, float h, float s, float l) + { + // Arrange + CieXyy input = new(x, y, yl); + Hsl expected = new(h, s, l); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + Hsl actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(120, 1, 0.2138507, 0.32114, 0.59787, 0.10976)] + public void Convert_Hsl_to_CieXyy(float h, float s, float l, float x, float y, float yl) + { + // Arrange + Hsl input = new(h, s, l); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs new file mode 100644 index 000000000..4e2bea427 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyyAndHsvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.42770)] + public void Convert_CieXyy_to_Hsv(float x, float y, float yl, float h, float s, float v) + { + // Arrange + CieXyy input = new(x, y, yl); + Hsv expected = new(h, s, v); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + Hsv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(120, 1, 0.42770, 0.32114, 0.59787, 0.10976)] + public void Convert_Hsv_to_CieXyy(float h, float s, float v, float x, float y, float yl) + { + // Arrange + Hsv input = new(h, s, v); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 9b99301cd8758e3e4c18341733d119679b7c200e Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 7 May 2024 23:51:20 +1000 Subject: [PATCH 29/79] Update CieXyzAndCieLabConversionTest.cs --- .../ColorProfiles/CieXyzAndCieLabConversionTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs index da2bcbc87..a88798881 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs @@ -24,7 +24,7 @@ public class CieXyzAndCieLabConversionTest [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)] + [InlineData(10, -400, 20, -0.08712, 0.01126, -0.00192)] public void Convert_Lab_to_Xyz(float l, float a, float b, float x, float y, float z) { // Arrange From 3ac5f703f3c56ee365380e54de52bee121906413 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 8 May 2024 12:24:03 +1000 Subject: [PATCH 30/79] Complete CieXyy tests --- .../CieXyyAndHunterLabConversionTests.cs | 70 +++++++++++++++++++ .../CieXyyAndLmsConversionTests.cs | 70 +++++++++++++++++++ .../CieXyyAndRgbConversionTests.cs | 70 +++++++++++++++++++ .../CieXyyAndYCbCrConversionTests.cs | 70 +++++++++++++++++++ 4 files changed, 280 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyyAndRgbConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyyAndYCbCrConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs new file mode 100644 index 000000000..a3b7d4b0f --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyyAndHunterLabConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 31.6467056, -33.00599, 25.67032)] + public void Convert_CieXyy_to_HunterLab(float x, float y, float yl, float l, float a, float b) + { + // Arrange + CieXyy input = new(x, y, yl); + HunterLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new HunterLab[5]; + + // Act + HunterLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(31.6467056, -33.00599, 25.67032, 0.360555, 0.936901, 0.1001514)] + public void Convert_HunterLab_to_CieXyy(float l, float a, float b, float x, float y, float yl) + { + // Arrange + HunterLab input = new(l, a, b); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs new file mode 100644 index 000000000..6974a864f --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +public class CieXyyAndLmsConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 0.06631134, 0.1415282, -0.03809926)] + public void Convert_CieXyy_to_Lms(float x, float y, float yl, float l, float m, float s) + { + // Arrange + CieXyy input = new(x, y, yl); + Lms expected = new(l, m, s); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new Lms[5]; + + // Act + Lms actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.06631134, 0.1415282, -0.03809926, 0.360555, 0.936901, 0.1001514)] + public void Convert_Lms_to_CieXyy(float l, float m, float s, float x, float y, float yl) + { + // Arrange + Lms input = new(l, m, s); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new Lms[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndRgbConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndRgbConversionTests.cs new file mode 100644 index 000000000..18df2ce14 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndRgbConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyyAndRgbConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 0, 0.4277014, 0)] + public void Convert_CieXyy_to_Rgb(float x, float y, float yl, float r, float g, float b) + { + // Arrange + CieXyy input = new(x, y, yl); + Rgb expected = new(r, g, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0, 0.4277014, 0, 0.32114, 0.59787, 0.10976)] + public void Convert_Rgb_to_CieXyy(float r, float g, float b, float x, float y, float yl) + { + // Arrange + Rgb input = new(r, g, b); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndYCbCrConversionTests.cs new file mode 100644 index 000000000..1fe359603 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndYCbCrConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyyAndYCbCrConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(0.360555, 0.936901, 0.1001514, 64.0204849, 91.87107, 82.33627)] + public void Convert_CieXyy_to_YCbCr(float x, float y, float yl, float y2, float cb, float cr) + { + // Arrange + CieXyy input = new(x, y, yl); + YCbCr expected = new(y2, cb, cr); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyy[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + YCbCr actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 128, 128, 0, 0, 0)] + [InlineData(64.0204849, 91.87107, 82.33627, 0.32114, 0.59787, 0.10976)] + public void Convert_YCbCr_to_CieXyy(float y2, float cb, float cr, float x, float y, float yl) + { + // Arrange + YCbCr input = new(y2, cb, cr); + CieXyy expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyy[5]; + + // Act + CieXyy actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From ba066f96b503e8f83196cdbb7a1fd2995076e0a6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 8 May 2024 14:30:06 +1000 Subject: [PATCH 31/79] Fix namespace add first LCH test --- .../CieLabAndCieLchuvConversionTests.cs | 2 +- .../CieLabAndCieLuvConversionTests.cs | 2 +- .../CieLabAndCieXyyConversionTests.cs | 2 +- .../CieLabAndCmykConversionTests.cs | 2 +- .../CieLabAndRgbConversionTests.cs | 2 +- .../CieLabAndYCbCrConversionTests.cs | 2 +- .../CieLchuvAndCieLchConversionTests.cs | 2 +- .../CieLchuvAndCieLuvConversionTests.cs | 2 +- .../CieXyyAndLmsConversionTests.cs | 2 +- .../CieXyzAndCieLchConversionTests.cs | 70 +++++++++++++++++++ .../CieXyzAndCieXyyConversionTest.cs | 2 +- .../CmykAndYCbCrConversionTests.cs | 2 +- .../RgbAndCieXyzConversionTest.cs | 2 +- .../ColorProfiles/RgbAndCmykConversionTest.cs | 2 +- .../RgbAndYCbCrConversionTest.cs | 2 +- 15 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchConversionTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs index 7bf7b4231..13016fe87 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs index fb9f25bd8..cd1d08f5d 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs index 074173da5..a464eeca1 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieXyyConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs index a13ad7d15..746671c4a 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCmykConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs index 3b0b2f96a..0a0453bc6 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndRgbConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs index 6f4d7ec42..9a29b1539 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndYCbCrConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs index 580e66e5e..5b94572f6 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs index a65c7e81c..43fd62355 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs index 6974a864f..1a89cb26d 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndLmsConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchConversionTests.cs new file mode 100644 index 000000000..bde3b8e17 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyzAndCieLchConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 97.50697, 161.235321, 143.157)] + public void Convert_CieXyz_to_CieLch(float x, float y, float yl, float l, float c, float h) + { + // Arrange + CieXyz input = new(x, y, yl); + CieLch expected = new(l, c, h); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + CieLch actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(97.50697, 161.235321, 143.157, 0.3605551, 0.936901, 0.1001514)] + public void Convert_CieLch_to_CieXyz(float l, float c, float h, float x, float y, float yl) + { + // Arrange + CieLch input = new(l, c, h); + CieXyz expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs index c4fa554a6..7b1d0ac78 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieXyyConversionTest.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs index ef3305ec7..df7490b96 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs index 5e867178a..783e38e7f 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs @@ -4,7 +4,7 @@ using SixLabors.ColorProfiles; using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs index 3ae2996f2..4f4ecb70b 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCmykConversionTest.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs index 05c17e89e..91f7fc08e 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndYCbCrConversionTest.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. From e67a67171c53eadbf76548475160f117ed3ed236 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 8 May 2024 19:59:15 +1000 Subject: [PATCH 32/79] Add some XYZ tests --- src/ImageSharp/ColorProfiles/CieLuv.cs | 10 +-- .../CieXyzAndCieLchuvConversionTests.cs | 69 +++++++++++++++ .../CieXyzAndCieLuvConversionTest.cs | 86 +++++++++++++++++++ 3 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchuvConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs diff --git a/src/ImageSharp/ColorProfiles/CieLuv.cs b/src/ImageSharp/ColorProfiles/CieLuv.cs index 8708ea255..304a14c7a 100644 --- a/src/ImageSharp/ColorProfiles/CieLuv.cs +++ b/src/ImageSharp/ColorProfiles/CieLuv.cs @@ -94,7 +94,7 @@ public readonly struct CieLuv : IColorProfile ? ((116 * Math.Pow(yr, e)) - 16d) : (CieConstants.Kappa * yr); - if (double.IsNaN(l)) + if (double.IsNaN(l) || l == -0d) { l = 0; } @@ -107,7 +107,7 @@ public readonly struct CieLuv : IColorProfile u = 0; } - if (double.IsNaN(v) || u == -0d) + if (double.IsNaN(v) || v == -0d) { v = 0; } @@ -151,17 +151,17 @@ public readonly struct CieLuv : IColorProfile double x = (d - b) / (a - c); double z = (x * a) + b; - if (double.IsNaN(x)) + if (double.IsNaN(x) || x == -0d) { x = 0; } - if (double.IsNaN(y)) + if (double.IsNaN(y) || y == -0d) { y = 0; } - if (double.IsNaN(z)) + if (double.IsNaN(z) || z == -0d) { z = 0; } diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchuvConversionTests.cs new file mode 100644 index 000000000..fa604250f --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLchuvConversionTests.cs @@ -0,0 +1,69 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyzAndCieLchuvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0.360555, 0.936901, 0.1001514, 97.50697, 177.345169, 142.601547)] + public void Convert_CieXyz_to_CieLchuv(float x, float y, float yl, float l, float c, float h) + { + // Arrange + CieXyz input = new(x, y, yl); + CieLchuv expected = new(l, c, h); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLchuv[5]; + + // Act + CieLchuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(97.50697, 177.345169, 142.601547, 0.360555, 0.936901, 0.1001514)] + public void Convert_CieLchuv_to_CieXyz(float l, float c, float h, float x, float y, float yl) + { + // Arrange + CieLchuv input = new(l, c, h); + CieXyz expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLchuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs new file mode 100644 index 000000000..68151eaa0 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs @@ -0,0 +1,86 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieXyzAndCieLuvConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0, 100, 50, 0, 0, 0)] + [InlineData(0.1, 100, 50, 0.000493, 0.000111, -0.000709)] + [InlineData(70.0000, 86.3525, 2.8240, 0.569310, 0.407494, 0.365843)] + [InlineData(10.0000, -1.2345, -10.0000, 0.012191, 0.011260, 0.025939)] + [InlineData(100, 0, 0, 0.950470, 1.000000, 1.088830)] + [InlineData(1, 1, 1, 0.001255, 0.001107, 0.000137)] + public void Convert_Luv_to_Xyz(float l, float u, float v, float x, float y, float z) + { + // Arrange + CieLuv input = new(l, u, v); + CieXyz expected = new(x, y, z); + + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.000493, 0.000111, 0, 0.1003, 0.9332, -0.0070)] + [InlineData(0.569310, 0.407494, 0.365843, 70.0000, 86.3524, 2.8240)] + [InlineData(0.012191, 0.011260, 0.025939, 9.9998, -1.2343, -9.9999)] + [InlineData(0.950470, 1.000000, 1.088830, 100, 0, 0)] + [InlineData(0.001255, 0.001107, 0.000137, 0.9999, 0.9998, 1.0004)] + public void Convert_Xyz_to_Luv(float x, float y, float z, float l, float u, float v) + { + // Arrange + CieXyz input = new(x, y, z); + CieLuv expected = new(l, u, v); + + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorProfileConverter converter = new(options); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 7d3f852245b0ec2012c8239940b2bb8ab2ffbbfc Mon Sep 17 00:00:00 2001 From: antonfirsov Date: Wed, 8 May 2024 18:10:29 +0200 Subject: [PATCH 33/79] Fix overflow in MemoryAllocator.Create(options) (#2730) Fix an overlook from #2706. See https://github.com/SixLabors/ImageSharp/commit/92b82779ac8e7a032989533bb9a01ef092186b2e#r141770676. --- .../Memory/Allocators/MemoryAllocator.cs | 2 +- .../UniformUnmanagedPoolMemoryAllocatorTests.cs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs index b56bedd04..8eaf0b6d6 100644 --- a/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs @@ -49,7 +49,7 @@ public abstract class MemoryAllocator UniformUnmanagedMemoryPoolMemoryAllocator allocator = new(options.MaximumPoolSizeMegabytes); if (options.AllocationLimitMegabytes.HasValue) { - allocator.MemoryGroupAllocationLimitBytes = options.AllocationLimitMegabytes.Value * 1024 * 1024; + allocator.MemoryGroupAllocationLimitBytes = options.AllocationLimitMegabytes.Value * 1024L * 1024L; allocator.SingleBufferAllocationLimitBytes = (int)Math.Min(allocator.SingleBufferAllocationLimitBytes, allocator.MemoryGroupAllocationLimitBytes); } diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs index 077d0afed..aa34a5c10 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs @@ -442,4 +442,20 @@ public class UniformUnmanagedPoolMemoryAllocatorTests allocator.AllocateGroup(4 * oneMb, 1024).Dispose(); // Should work Assert.Throws(() => allocator.AllocateGroup(5 * oneMb, 1024)); } + + [ConditionalFact(typeof(Environment), nameof(Environment.Is64BitProcess))] + public void MemoryAllocator_Create_SetHighLimit() + { + RemoteExecutor.Invoke(RunTest).Dispose(); + static void RunTest() + { + const long threeGB = 3L * (1 << 30); + MemoryAllocator allocator = MemoryAllocator.Create(new MemoryAllocatorOptions() + { + AllocationLimitMegabytes = (int)(threeGB / 1024) + }); + using MemoryGroup memoryGroup = allocator.AllocateGroup(threeGB, 1024); + Assert.Equal(threeGB, memoryGroup.TotalLength); + } + } } From 9fdf204086a399aa6991e62440e92d223a8d8078 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 10 May 2024 23:53:24 +1000 Subject: [PATCH 34/79] Complete conversion tests --- src/ImageSharp/ColorProfiles/CieLuv.cs | 10 +-- ...olorProfileConverterExtensionsCieLabRgb.cs | 57 +++++++++++++ ...olorProfileConverterExtensionsRgbCieLab.cs | 57 +++++++++++++ src/ImageSharp/ColorProfiles/Hsl.cs | 2 +- src/ImageSharp/ColorProfiles/Hsv.cs | 6 +- .../ColorProfiles/LmsAdaptationMatrix.cs | 2 +- .../CieXyyAndHsvConversionTests.cs | 4 +- .../CieXyyAndHunterLabConversionTests.cs | 4 +- .../CieXyzAndCieLuvConversionTest.cs | 49 +++++------ .../CieXyzAndHslConversionTests.cs | 70 ++++++++++++++++ .../CieXyzAndHsvConversionTests.cs | 70 ++++++++++++++++ .../CieXyzAndHunterLabConversionTest.cs | 74 +++++++++++++++++ .../CieXyzAndLmsConversionTest.cs | 4 +- .../CieXyzAndYCbCrConversionTests.cs | 70 ++++++++++++++++ .../CmykAndCieLchConversionTests.cs | 69 +++++++++++++++ .../CmykAndCieLuvConversionTests.cs | 70 ++++++++++++++++ .../CmykAndYCbCrConversionTests.cs | 4 +- .../RgbAndCieXyzConversionTest.cs | 8 +- .../ColorProfiles/RgbAndHslConversionTest.cs | 83 +++++++++++++++++++ .../ColorProfiles/RgbAndHsvConversionTest.cs | 81 ++++++++++++++++++ 20 files changed, 745 insertions(+), 49 deletions(-) create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs create mode 100644 src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHslConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHsvConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHunterLabConversionTest.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzAndYCbCrConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLchConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLuvConversionTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/RgbAndHsvConversionTest.cs diff --git a/src/ImageSharp/ColorProfiles/CieLuv.cs b/src/ImageSharp/ColorProfiles/CieLuv.cs index 304a14c7a..64541e496 100644 --- a/src/ImageSharp/ColorProfiles/CieLuv.cs +++ b/src/ImageSharp/ColorProfiles/CieLuv.cs @@ -46,11 +46,6 @@ public readonly struct CieLuv : IColorProfile /// public readonly float V { get; } - /// - /// Gets the reference white point of this color - /// - public readonly CieXyz WhitePoint { get; } - /// /// Compares two objects for equality. /// @@ -185,7 +180,7 @@ public readonly struct CieLuv : IColorProfile => ChromaticAdaptionWhitePointSource.WhitePoint; /// - public override int GetHashCode() => HashCode.Combine(this.L, this.U, this.V, this.WhitePoint); + public override int GetHashCode() => HashCode.Combine(this.L, this.U, this.V); /// public override string ToString() => FormattableString.Invariant($"CieLuv({this.L:#0.##}, {this.U:#0.##}, {this.V:#0.##})"); @@ -198,8 +193,7 @@ public readonly struct CieLuv : IColorProfile public bool Equals(CieLuv other) => this.L.Equals(other.L) && this.U.Equals(other.U) - && this.V.Equals(other.V) - && this.WhitePoint.Equals(other.WhitePoint); + && this.V.Equals(other.V); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double ComputeU(in CieXyz source) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs new file mode 100644 index 000000000..bbedb2e2b --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs @@ -0,0 +1,57 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsCieLabRgb +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + CieLab pcsFromA = source.ToProfileConnectingSpace(options); + CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); + + // Adapt to target white point + pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + + // Convert between PCS + Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromAOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFromA = pcsFromAOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFromA); + + using IMemoryOwner pcsFromBOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFromB = pcsFromBOwner.GetSpan(); + CieLab.ToProfileConnectionSpace(options, pcsFromA, pcsFromB); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsFromB, pcsFromB); + + // Convert between PCS. + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsTo = pcsToOwner.GetSpan(); + Rgb.FromProfileConnectionSpace(options, pcsFromB, pcsTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsTo, destination); + } +} diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs new file mode 100644 index 000000000..baa92755c --- /dev/null +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs @@ -0,0 +1,57 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Buffers; +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.ColorProfiles; + +internal static class ColorProfileConverterExtensionsRgbCieLab +{ + public static TTo Convert(this ColorProfileConverter converter, TFrom source) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS + Rgb pcsFromA = source.ToProfileConnectingSpace(options); + CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); + + // Adapt to target white point + pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + + // Convert between PCS + CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB); + + // Convert to output from PCS + return TTo.FromProfileConnectingSpace(options, pcsTo); + } + + public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + ColorConversionOptions options = converter.Options; + + // Convert to input PCS. + using IMemoryOwner pcsFromAOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFromA = pcsFromAOwner.GetSpan(); + TFrom.ToProfileConnectionSpace(options, source, pcsFromA); + + using IMemoryOwner pcsFromBOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsFromB = pcsFromBOwner.GetSpan(); + Rgb.ToProfileConnectionSpace(options, pcsFromA, pcsFromB); + + // Adapt to target white point + VonKriesChromaticAdaptation.Transform(options, pcsFromB, pcsFromB); + + // Convert between PCS. + using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); + Span pcsTo = pcsToOwner.GetSpan(); + CieLab.FromProfileConnectionSpace(options, pcsFromB, pcsTo); + + // Convert to output from PCS + TTo.FromProfileConnectionSpace(options, pcsTo, destination); + } +} diff --git a/src/ImageSharp/ColorProfiles/Hsl.cs b/src/ImageSharp/ColorProfiles/Hsl.cs index dd8772d96..8dcef23c4 100644 --- a/src/ImageSharp/ColorProfiles/Hsl.cs +++ b/src/ImageSharp/ColorProfiles/Hsl.cs @@ -114,7 +114,7 @@ public readonly struct Hsl : IColorProfile } h *= 60F; - if (h < 0F) + if (h < -Constants.Epsilon) { h += 360F; } diff --git a/src/ImageSharp/ColorProfiles/Hsv.cs b/src/ImageSharp/ColorProfiles/Hsv.cs index 787b8ad41..b3922d11f 100644 --- a/src/ImageSharp/ColorProfiles/Hsv.cs +++ b/src/ImageSharp/ColorProfiles/Hsv.cs @@ -111,10 +111,10 @@ public readonly struct Hsv : IColorProfile h = 4 + ((r - g) / chroma); } - h *= 60; - if (h < 0f) + h *= 60F; + if (h < -Constants.Epsilon) { - h += 360; + h += 360F; } s = chroma / v; diff --git a/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs b/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs index 70bc5aef2..b5b2887cc 100644 --- a/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs +++ b/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// /// /// Matrix data obtained from: -/// Two New von Kries Based Chromatic Adaptation Transforms Found by Numerical Optimization +/// 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 /// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs index 4e2bea427..c2547ca84 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHsvConversionTests.cs @@ -15,7 +15,7 @@ public class CieXyyAndHsvConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.42770)] - public void Convert_CieXyy_to_Hsv(float x, float y, float yl, float h, float s, float v) + public void Convert_CieXyy_To_Hsv(float x, float y, float yl, float h, float s, float v) { // Arrange CieXyy input = new(x, y, yl); @@ -43,7 +43,7 @@ public class CieXyyAndHsvConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(120, 1, 0.42770, 0.32114, 0.59787, 0.10976)] - public void Convert_Hsv_to_CieXyy(float h, float s, float v, float x, float y, float yl) + public void Convert_Hsv_To_CieXyy(float h, float s, float v, float x, float y, float yl) { // Arrange Hsv input = new(h, s, v); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs index a3b7d4b0f..4f66538f0 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyAndHunterLabConversionTests.cs @@ -15,7 +15,7 @@ public class CieXyyAndHunterLabConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(0.360555, 0.936901, 0.1001514, 31.6467056, -33.00599, 25.67032)] - public void Convert_CieXyy_to_HunterLab(float x, float y, float yl, float l, float a, float b) + public void Convert_CieXyy_To_HunterLab(float x, float y, float yl, float l, float a, float b) { // Arrange CieXyy input = new(x, y, yl); @@ -43,7 +43,7 @@ public class CieXyyAndHunterLabConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(31.6467056, -33.00599, 25.67032, 0.360555, 0.936901, 0.1001514)] - public void Convert_HunterLab_to_CieXyy(float l, float a, float b, float x, float y, float yl) + public void Convert_HunterLab_To_CieXyy(float l, float a, float b, float x, float y, float yl) { // Arrange HunterLab input = new(l, a, b); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs index 68151eaa0..4a6c3c4f8 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs @@ -18,29 +18,28 @@ public class CieXyzAndCieLuvConversionTest [Theory] [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0, 100, 50, 0, 0, 0)] - [InlineData(0.1, 100, 50, 0.000493, 0.000111, -0.000709)] - [InlineData(70.0000, 86.3525, 2.8240, 0.569310, 0.407494, 0.365843)] - [InlineData(10.0000, -1.2345, -10.0000, 0.012191, 0.011260, 0.025939)] - [InlineData(100, 0, 0, 0.950470, 1.000000, 1.088830)] - [InlineData(1, 1, 1, 0.001255, 0.001107, 0.000137)] - public void Convert_Luv_to_Xyz(float l, float u, float v, float x, float y, float z) + [InlineData(0.000493, 0.000111, 0, 0.10026589, 0.9332349, -0.00704865158)] + [InlineData(0.569310, 0.407494, 0.365843, 70.0000, 86.3524, 2.8240)] + [InlineData(0.012191, 0.011260, 0.025939, 9.9998, -1.2343, -9.9999)] + [InlineData(0.950470, 1.000000, 1.088830, 100, 0, 0)] + [InlineData(0.001255, 0.001107, 0.000137, 0.9999, 0.9998, 1.0004)] + public void Convert_Xyz_To_Luv(float x, float y, float z, float l, float u, float v) { // Arrange - CieLuv input = new(l, u, v); - CieXyz expected = new(x, y, z); + CieXyz input = new(x, y, z); + CieLuv expected = new(l, u, v); ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; ColorProfileConverter converter = new(options); - Span inputSpan = new CieLuv[5]; + Span inputSpan = new CieXyz[5]; inputSpan.Fill(input); - Span actualSpan = new CieXyz[5]; + Span actualSpan = new CieLuv[5]; // Act - CieXyz actual = converter.Convert(input); - converter.Convert(inputSpan, actualSpan); + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); // Assert Assert.Equal(expected, actual, Comparer); @@ -53,27 +52,29 @@ public class CieXyzAndCieLuvConversionTest [Theory] [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.000493, 0.000111, 0, 0.1003, 0.9332, -0.0070)] - [InlineData(0.569310, 0.407494, 0.365843, 70.0000, 86.3524, 2.8240)] - [InlineData(0.012191, 0.011260, 0.025939, 9.9998, -1.2343, -9.9999)] - [InlineData(0.950470, 1.000000, 1.088830, 100, 0, 0)] - [InlineData(0.001255, 0.001107, 0.000137, 0.9999, 0.9998, 1.0004)] - public void Convert_Xyz_to_Luv(float x, float y, float z, float l, float u, float v) + [InlineData(0, 100, 50, 0, 0, 0)] + [InlineData(0.1, 100, 50, 0.000493, 0.000111, -0.000709)] + [InlineData(70.0000, 86.3525, 2.8240, 0.569310, 0.407494, 0.365843)] + [InlineData(10.0000, -1.2345, -10.0000, 0.012191, 0.011260, 0.025939)] + [InlineData(100, 0, 0, 0.950470, 1.000000, 1.088830)] + [InlineData(1, 1, 1, 0.001255, 0.001107, 0.000137)] + public void Convert_Luv_To_Xyz(float l, float u, float v, float x, float y, float z) { // Arrange - CieXyz input = new(x, y, z); - CieLuv expected = new(l, u, v); + CieLuv input = new(l, u, v); + CieXyz expected = new(x, y, z); ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; ColorProfileConverter converter = new(options); - Span inputSpan = new CieXyz[5]; + Span inputSpan = new CieLuv[5]; inputSpan.Fill(input); - Span actualSpan = new CieLuv[5]; + Span actualSpan = new CieXyz[5]; // Act - CieLuv actual = converter.Convert(input); + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); // Assert Assert.Equal(expected, actual, Comparer); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHslConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHslConversionTests.cs new file mode 100644 index 000000000..cffdb008b --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHslConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyzAndHslConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.5)] + public void Convert_CieXyz_to_Hsl(float x, float y, float yl, float h, float s, float l) + { + // Arrange + CieXyz input = new(x, y, yl); + Hsl expected = new(h, s, l); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + Hsl actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(120, 1, 0.5, 0.38506496, 0.716878653, 0.09710451)] + public void Convert_Hsl_to_CieXyz(float h, float s, float l, float x, float y, float yl) + { + // Arrange + Hsl input = new(h, s, l); + CieXyz expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHsvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHsvConversionTests.cs new file mode 100644 index 000000000..d4a0022a4 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHsvConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyzAndHsvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.9999999)] + public void Convert_CieXyz_to_Hsv(float x, float y, float yl, float h, float s, float v) + { + // Arrange + CieXyz input = new(x, y, yl); + Hsv expected = new(h, s, v); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + Hsv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(120, 1, 0.9999999, 0.3850648, 0.7168785, 0.09710446)] + public void Convert_Hsv_to_CieXyz(float h, float s, float v, float x, float y, float yl) + { + // Arrange + Hsv input = new(h, s, v); + CieXyz expected = new(x, y, yl); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHunterLabConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHunterLabConversionTest.cs new file mode 100644 index 000000000..aef26fe9a --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndHunterLabConversionTest.cs @@ -0,0 +1,74 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class CieXyzAndHunterLabConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.360555, 0.936901, 0.1001514, 96.79365, -100.951096, 49.35507)] + public void Convert_Xyz_To_HunterLab(float x, float y, float z, float l, float a, float b) + { + // Arrange + CieXyz input = new(x, y, z); + HunterLab expected = new(l, a, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new HunterLab[5]; + + // Act + HunterLab actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(31.6467056, -33.00599, 25.67032, 0.0385420471, 0.10015139, -0.0317969956)] + public void Convert_HunterLab_To_Xyz(float l, float a, float b, float x, float y, float z) + { + // Arrange + HunterLab input = new(l, a, b); + CieXyz expected = new(x, y, z); + ColorProfileConverter converter = new(); + + Span inputSpan = new HunterLab[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs index 60938991e..185fcd256 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndLmsConversionTest.cs @@ -22,7 +22,7 @@ public class CieXyzAndLmsConversionTest [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) + public void Convert_Lms_To_CieXyz(float l, float m, float s, float x, float y, float z) { // Arrange Lms input = new(l, m, s); @@ -54,7 +54,7 @@ public class CieXyzAndLmsConversionTest [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) + public void Convert_CieXyz_To_Lms(float x, float y, float z, float l, float m, float s) { // Arrange CieXyz input = new(x, y, z); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndYCbCrConversionTests.cs new file mode 100644 index 000000000..475673da8 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndYCbCrConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CieXyzAndYCbCrConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 128, 128)] + [InlineData(0.360555, 0.936901, 0.1001514, 149.685, 43.52769, 21.23457)] + public void Convert_CieXyz_to_YCbCr(float x, float y, float z, float y2, float cb, float cr) + { + // Arrange + CieXyz input = new(x, y, z); + YCbCr expected = new(y2, cb, cr); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new YCbCr[5]; + + // Act + YCbCr actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 128, 128, 0, 0, 0)] + [InlineData(149.685, 43.52769, 21.23457, 0.38506496, 0.716878653, 0.0971045)] + public void Convert_YCbCr_to_CieXyz(float y2, float cb, float cr, float x, float y, float z) + { + // Arrange + YCbCr input = new(y2, cb, cr); + CieXyz expected = new(x, y, z); + ColorProfileConverter converter = new(); + + Span inputSpan = new YCbCr[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + // Act + CieXyz actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLchConversionTests.cs new file mode 100644 index 000000000..a5230eb31 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLchConversionTests.cs @@ -0,0 +1,69 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CmykAndCieLchConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 62.85025, 64.77041, 118.2425)] + public void Convert_Cmyk_To_CieLch(float c, float m, float y, float k, float l, float c2, float h) + { + // Arrange + Cmyk input = new(c, m, y, k); + CieLch expected = new(l, c2, h); + ColorProfileConverter converter = new(); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLch[5]; + + // Act + CieLch actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(100, 3.81656E-05, 218.6598, 0, 1.192093E-07, 0, 5.960464E-08)] + [InlineData(62.85025, 64.77041, 118.2425, 0.286581, 0, 0.7975187, 0.34983)] + public void Convert_CieLch_To_Cmyk(float l, float c2, float h, float c, float m, float y, float k) + { + // Arrange + CieLch input = new(l, c2, h); + Cmyk expected = new(c, m, y, k); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLch[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + Cmyk actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLuvConversionTests.cs new file mode 100644 index 000000000..cfbd08054 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CmykAndCieLuvConversionTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +public class CmykAndCieLuvConversionTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0002f); + + [Theory] + [InlineData(0, 0, 0, 0, 100, -1.937151E-05, -3.874302E-05)] + [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 62.85024, -24.4844189, 54.8588524)] + public void Convert_Cmyk_To_CieLuv(float c, float m, float y, float k, float l, float u, float v) + { + // Arrange + Cmyk input = new(c, m, y, k); + CieLuv expected = new(l, u, v); + ColorProfileConverter converter = new(); + + Span inputSpan = new Cmyk[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieLuv[5]; + + // Act + CieLuv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(100, -1.937151E-05, -3.874302E-05, 0, 5.96046448E-08, 0, 0)] + [InlineData(62.85024, -24.4844189, 54.8588524, 0.2865809, 0, 0.797518551, 0.3498301)] + public void Convert_CieLuv_To_Cmyk(float l, float u, float v, float c, float m, float y, float k) + { + // Arrange + CieLuv input = new(l, u, v); + Cmyk expected = new(c, m, y, k); + ColorProfileConverter converter = new(); + + Span inputSpan = new CieLuv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Cmyk[5]; + + // Act + Cmyk actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs index df7490b96..64b47e2b9 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CmykAndYCbCrConversionTests.cs @@ -15,7 +15,7 @@ public class CmykAndYCbCrConversionTests [Theory] [InlineData(0, 0, 0, 0, 255, 128, 128)] [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 136.5134, 69.90555, 114.9948)] - public void Convert_Cmyk_to_YCbCr(float c, float m, float y, float k, float y2, float cb, float cr) + public void Convert_Cmyk_To_YCbCr(float c, float m, float y, float k, float y2, float cb, float cr) { // Arrange Cmyk input = new(c, m, y, k); @@ -43,7 +43,7 @@ public class CmykAndYCbCrConversionTests [Theory] [InlineData(255, 128, 128, 0, 0, 0, 5.960464E-08)] [InlineData(136.5134, 69.90555, 114.9948, 0.2891567, 0, 0.7951807, 0.3490196)] - public void Convert_YCbCr_to_Cmyk(float y2, float cb, float cr, float c, float m, float y, float k) + public void Convert_YCbCr_To_Cmyk(float y2, float cb, float cr, float c, float m, float y, float k) { // Arrange YCbCr input = new(y2, cb, cr); diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs index 783e38e7f..f84892561 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs @@ -24,7 +24,7 @@ public class RgbAndCieXyzConversionTest [InlineData(0.00000, 0.00000, 0.82521, 0, 0.181415, 1)] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(0.297676, 0.267854, 0.045504, 0.720315, 0.509999, 0.168112)] - public void Convert_XYZ_D50_to_SRGB(float x, float y, float z, float r, float g, float b) + public void Convert_XYZ_D50_To_SRGB(float x, float y, float z, float r, float g, float b) { // Arrange CieXyz input = new(x, y, z); @@ -57,7 +57,7 @@ public class RgbAndCieXyzConversionTest [InlineData(0, 0, 1.088830, 0, 0.235458, 1)] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(0.297676, 0.267854, 0.045504, 0.754903, 0.501961, 0.099998)] - public void Convert_XYZ_D65_to_SRGB(float x, float y, float z, float r, float g, float b) + public void Convert_XYZ_D65_To_SRGB(float x, float y, float z, float r, float g, float b) { // Arrange CieXyz input = new(x, y, z); @@ -90,7 +90,7 @@ public class RgbAndCieXyzConversionTest [InlineData(0, 1, 0, 0.385065, 0.716879, 0.0971045)] [InlineData(0, 0, 1, 0.143080, 0.060617, 0.714173)] [InlineData(0.754902, 0.501961, 0.100000, 0.315757, 0.273323, 0.035506)] - public void Convert_SRGB_to_XYZ_D50(float r, float g, float b, float x, float y, float z) + public void Convert_SRGB_To_XYZ_D50(float r, float g, float b, float x, float y, float z) { // Arrange Rgb input = new(r, g, b); @@ -123,7 +123,7 @@ public class RgbAndCieXyzConversionTest [InlineData(0, 1, 0, 0.357576, 0.715152, 0.119192)] [InlineData(0, 0, 1, 0.1804375, 0.072175, 0.950304)] [InlineData(0.754902, 0.501961, 0.100000, 0.297676, 0.267854, 0.045504)] - public void Convert_SRGB_to_XYZ_D65(float r, float g, float b, float x, float y, float z) + public void Convert_SRGB_To_XYZ_D65(float r, float g, float b, float x, float y, float z) { // Arrange Rgb input = new(r, g, b); diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs new file mode 100644 index 000000000..b63b9ca84 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs @@ -0,0 +1,83 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +/// +public class RgbAndHslConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0, 1, 1, 1, 1, 1)] + [InlineData(360, 1, 1, 1, 1, 1)] + [InlineData(0, 1, .5F, 1, 0, 0)] + [InlineData(120, 1, .5F, 0, 1, 0)] + [InlineData(240, 1, .5F, 0, 0, 1)] + public void Convert_Hsl_To_Rgb(float h, float s, float l, float r, float g, float b) + { + // Arrange + Hsl input = new(h, s, l); + Rgb expected = new(r, g, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsl[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 1, 1, 0, 0, 1)] + [InlineData(1, 0, 0, 0, 1, .5F)] + [InlineData(0, 1, 0, 120, 1, .5F)] + [InlineData(0, 0, 1, 240, 1, .5F)] + [InlineData(0.7, 0.8, 0.6, 90, 0.3333, 0.7F)] + public void Convert_Rgb_To_Hsl(float r, float g, float b, float h, float s, float l) + { + // Arrange + Rgb input = new(r, g, b); + Hsl expected = new(h, s, l); + ColorProfileConverter converter = new(); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsl[5]; + + // Act + Hsl actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndHsvConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHsvConversionTest.cs new file mode 100644 index 000000000..b89b576b6 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHsvConversionTest.cs @@ -0,0 +1,81 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests - conversions. +/// +/// +/// Test data generated using: +/// +/// +public class RgbAndHsvConversionTest +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001f); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0, 0, 1, 1, 1, 1)] + [InlineData(360, 1, 1, 1, 0, 0)] + [InlineData(0, 1, 1, 1, 0, 0)] + [InlineData(120, 1, 1, 0, 1, 0)] + [InlineData(240, 1, 1, 0, 0, 1)] + public void Convert_Hsv_To_Rgb(float h, float s, float v, float r, float g, float b) + { + // Arrange + Hsv input = new(h, s, v); + Rgb expected = new(r, g, b); + ColorProfileConverter converter = new(); + + Span inputSpan = new Hsv[5]; + inputSpan.Fill(input); + + Span actualSpan = new Rgb[5]; + + // Act + Rgb actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 1, 1, 0, 0, 1)] + [InlineData(1, 0, 0, 0, 1, 1)] + [InlineData(0, 1, 0, 120, 1, 1)] + [InlineData(0, 0, 1, 240, 1, 1)] + public void Convert_Rgb_To_Hsv(float r, float g, float b, float h, float s, float v) + { + // Arrange + Rgb input = new(r, g, b); + Hsv expected = new(h, s, v); + ColorProfileConverter converter = new(); + + Span inputSpan = new Rgb[5]; + inputSpan.Fill(input); + + Span actualSpan = new Hsv[5]; + + // Act + Hsv actual = converter.Convert(input); + converter.Convert(inputSpan, actualSpan); + + // Assert + Assert.Equal(expected, actual, Comparer); + + for (int i = 0; i < actualSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From a7edf1b41a5b17953bd1ece51a51be6247f276a3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 11 May 2024 00:04:39 +1000 Subject: [PATCH 35/79] Some renaming and docs --- .../ColorProfiles/ColorConversionOptions.cs | 7 +++--- ...cs => KnownChromaticAdaptationMatrices.cs} | 22 +++++++++++-------- ...kingSpaces.cs => KnownRgbWorkingSpaces.cs} | 4 ++-- .../RgbAndCieXyzConversionTest.cs | 8 +++---- 4 files changed, 23 insertions(+), 18 deletions(-) rename src/ImageSharp/ColorProfiles/{LmsAdaptationMatrix.cs => KnownChromaticAdaptationMatrices.cs} (77%) rename src/ImageSharp/ColorProfiles/{RgbWorkingSpaces.cs => KnownRgbWorkingSpaces.cs} (99%) diff --git a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs index 384ab6a7f..920fdd800 100644 --- a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs +++ b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs @@ -18,7 +18,7 @@ public class ColorConversionOptions /// /// Initializes a new instance of the class. /// - public ColorConversionOptions() => this.AdaptationMatrix = LmsAdaptationMatrix.Bradford; + public ColorConversionOptions() => this.AdaptationMatrix = KnownChromaticAdaptationMatrices.Bradford; /// /// Gets the memory allocator. @@ -38,15 +38,16 @@ public class ColorConversionOptions /// /// Gets the source working space used for companding in conversions from/to XYZ color space. /// - public RgbWorkingSpace RgbWorkingSpace { get; init; } = RgbWorkingSpaces.SRgb; + public RgbWorkingSpace RgbWorkingSpace { get; init; } = KnownRgbWorkingSpaces.SRgb; /// /// Gets the destination working space used for companding in conversions from/to XYZ color space. /// - public RgbWorkingSpace TargetRgbWorkingSpace { get; init; } = RgbWorkingSpaces.SRgb; + public RgbWorkingSpace TargetRgbWorkingSpace { get; init; } = KnownRgbWorkingSpaces.SRgb; /// /// Gets the transformation matrix used in conversion to perform chromatic adaptation. + /// for further information. Default is Bradford. /// public Matrix4x4 AdaptationMatrix { diff --git a/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs b/src/ImageSharp/ColorProfiles/KnownChromaticAdaptationMatrices.cs similarity index 77% rename from src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs rename to src/ImageSharp/ColorProfiles/KnownChromaticAdaptationMatrices.cs index b5b2887cc..cbc76332b 100644 --- a/src/ImageSharp/ColorProfiles/LmsAdaptationMatrix.cs +++ b/src/ImageSharp/ColorProfiles/KnownChromaticAdaptationMatrices.cs @@ -6,19 +6,23 @@ using System.Numerics; namespace SixLabors.ImageSharp.ColorProfiles; /// -/// Matrices used for transformation from to , defining the cone response domain. +/// Provides matrices for chromatic adaptation, facilitating the adjustment of color values +/// under different light sources to maintain color constancy. This class supports common +/// adaptation transforms based on the von Kries coefficient law, which assumes independent +/// scaling of the cone responses in the human eye. These matrices can be applied to convert +/// color coordinates between different illuminants, ensuring consistent color appearance +/// across various lighting conditions. /// /// -/// Matrix 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 -/// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf +/// Supported adaptation matrices include the Bradford, von Kries, and Sharp transforms. +/// These matrices are typically used in conjunction with color space conversions, such as from XYZ +/// to RGB, to achieve accurate color rendition in digital imaging applications. /// -public static class LmsAdaptationMatrix + +public static class KnownChromaticAdaptationMatrices { /// - /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65) + /// von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65) /// public static readonly Matrix4x4 VonKriesHPEAdjusted = Matrix4x4.Transpose(new Matrix4x4 @@ -36,7 +40,7 @@ public static class LmsAdaptationMatrix }); /// - /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez for equal energy) + /// von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez for equal energy) /// public static readonly Matrix4x4 VonKriesHPE = Matrix4x4.Transpose(new Matrix4x4 diff --git a/src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs b/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs similarity index 99% rename from src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs rename to src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs index c0d7f5074..3b15c7fe6 100644 --- a/src/ImageSharp/ColorProfiles/RgbWorkingSpaces.cs +++ b/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.ColorProfiles; @@ -10,7 +10,7 @@ namespace SixLabors.ColorProfiles; /// /// Chromaticity coordinates based on: /// -public static class RgbWorkingSpaces +public static class KnownRgbWorkingSpaces { /// /// sRgb working space. diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs index f84892561..31f8a9478 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs @@ -28,7 +28,7 @@ public class RgbAndCieXyzConversionTest { // Arrange CieXyz input = new(x, y, z); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); Rgb expected = new(r, g, b); @@ -61,7 +61,7 @@ public class RgbAndCieXyzConversionTest { // Arrange CieXyz input = new(x, y, z); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); Rgb expected = new(r, g, b); @@ -94,7 +94,7 @@ public class RgbAndCieXyzConversionTest { // Arrange Rgb input = new(r, g, b); - ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D50, RgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D50, RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); CieXyz expected = new(x, y, z); @@ -127,7 +127,7 @@ public class RgbAndCieXyzConversionTest { // Arrange Rgb input = new(r, g, b); - ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D65, RgbWorkingSpace = RgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D65, RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); CieXyz expected = new(x, y, z); From fd77d2587e3590e51f0b15a58abb9afa417abe33 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 11 May 2024 11:55:47 +1000 Subject: [PATCH 36/79] Update build-and-test.yml --- .github/workflows/build-and-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 5a2e11266..e17b8d724 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -25,7 +25,7 @@ jobs: sdk-preview: true runtime: -x64 codecov: false - - os: macos-latest + - os: macos-13 # macos-latest runs on arm64 runners where libgdiplus is unavailable framework: net7.0 sdk: 7.0.x sdk-preview: true @@ -48,7 +48,7 @@ jobs: sdk: 6.0.x runtime: -x64 codecov: false - - os: macos-latest + - os: macos-13 # macos-latest runs on arm64 runners where libgdiplus is unavailable framework: net6.0 sdk: 6.0.x runtime: -x64 From bf13f0095cf0b7f8261724e6b925a017f90e7957 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 12 May 2024 22:17:36 +1000 Subject: [PATCH 37/79] Naming and fixes --- src/ImageSharp/ColorProfiles/CieLab.cs | 2 +- .../ColorProfiles/ColorConversionOptions.cs | 4 +- .../ColorProfiles/ColorProfileConverter.cs | 15 ++ ...rProfileConverterExtensionsCieLabCieLab.cs | 8 +- ...rProfileConverterExtensionsCieLabCieXyz.cs | 6 +- ...olorProfileConverterExtensionsCieLabRgb.cs | 6 +- ...rProfileConverterExtensionsCieXyzCieLab.cs | 6 +- ...rProfileConverterExtensionsCieXyzCieXyz.cs | 6 +- ...olorProfileConverterExtensionsCieXyzRgb.cs | 8 +- ...olorProfileConverterExtensionsRgbCieLab.cs | 6 +- ...olorProfileConverterExtensionsRgbCieXyz.cs | 8 +- .../ColorProfileConverterExtensionsRgbRgb.cs | 6 +- src/ImageSharp/ColorProfiles/HunterLab.cs | 4 +- .../{Illuminants.cs => KnownIlluminants.cs} | 4 +- .../ColorProfiles/KnownRgbWorkingSpaces.cs | 38 ++-- .../VonKriesChromaticAdaptation.cs | 55 ++--- .../CieLabAndCieLchConversionTests.cs | 4 +- .../CieLabAndCieLchuvConversionTests.cs | 4 +- .../CieLabAndCieLuvConversionTests.cs | 4 +- .../CieLchAndCieLuvConversionTests.cs | 4 +- .../CieLchuvAndCieLchConversionTests.cs | 4 +- .../CieLchuvAndCieLuvConversionTests.cs | 4 +- .../CieLchuvAndCmykConversionTests.cs | 4 +- .../CieLuvAndCieXyyConversionTests.cs | 4 +- .../CieLuvAndHslConversionTests.cs | 4 +- .../CieLuvAndHsvConversionTests.cs | 4 +- .../CieLuvAndHunterLabConversionTests.cs | 4 +- .../CieLuvAndLmsConversionTests.cs | 4 +- .../CieLuvAndRgbConversionTests.cs | 4 +- .../CieLuvAndYCbCrConversionTests.cs | 4 +- .../CieXyzAndCieLabConversionTest.cs | 4 +- .../CieXyzAndCieLuvConversionTest.cs | 4 +- ...ProfileConverterChomaticAdaptationTests.cs | 204 ++++++++++++++++++ .../RgbAndCieXyzConversionTest.cs | 8 +- .../VonKriesChromaticAdaptationTests.cs | 42 ++++ 35 files changed, 382 insertions(+), 118 deletions(-) rename src/ImageSharp/ColorProfiles/{Illuminants.cs => KnownIlluminants.cs} (96%) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/VonKriesChromaticAdaptationTests.cs diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs index 5cda362d4..d7dd7415d 100644 --- a/src/ImageSharp/ColorProfiles/CieLab.cs +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -16,7 +16,7 @@ public readonly struct CieLab : IProfileConnectingSpace /// D50 standard illuminant. /// Used when reference white is not specified explicitly. /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; + public static readonly CieXyz DefaultWhitePoint = KnownIlluminants.D50; /// /// Initializes a new instance of the struct. diff --git a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs index 920fdd800..93eb8bde1 100644 --- a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs +++ b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs @@ -28,12 +28,12 @@ public class ColorConversionOptions /// /// Gets the source white point used for chromatic adaptation in conversions from/to XYZ color space. /// - public CieXyz WhitePoint { get; init; } = Illuminants.D50; + public CieXyz WhitePoint { get; init; } = KnownIlluminants.D50; /// /// Gets the destination white point used for chromatic adaptation in conversions from/to XYZ color space. /// - public CieXyz TargetWhitePoint { get; init; } = Illuminants.D50; + public CieXyz TargetWhitePoint { get; init; } = KnownIlluminants.D50; /// /// Gets the source working space used for companding in conversions from/to XYZ color space. diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs index af9ab0af8..18b90a622 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverter.cs @@ -27,4 +27,19 @@ public class ColorProfileConverter /// Gets the color profile conversion options. /// public ColorConversionOptions Options { get; } + + internal (CieXyz From, CieXyz To) GetChromaticAdaptionWhitePoints() + where TFrom : struct, IColorProfile + where TTo : struct, IColorProfile + { + CieXyz sourceWhitePoint = TFrom.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint + ? this.Options.WhitePoint + : this.Options.RgbWorkingSpace.WhitePoint; + + CieXyz targetWhitePoint = TTo.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint + ? this.Options.TargetWhitePoint + : this.Options.TargetRgbWorkingSpace.WhitePoint; + + return (sourceWhitePoint, targetWhitePoint); + } } diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs index 51c68531d..9bfccb62b 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Buffers; @@ -19,7 +19,8 @@ internal static class ColorProfileConverterExtensionsCieLabCieLab CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); // Adapt to target white point - pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix); // Convert between PCS CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB); @@ -44,7 +45,8 @@ internal static class ColorProfileConverterExtensionsCieLabCieLab CieLab.ToProfileConnectionSpace(options, pcsFromTo, pcsFrom); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix); // Convert between PCS. CieLab.FromProfileConnectionSpace(options, pcsFrom, pcsFromTo); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs index 545b5dadf..4c86c8793 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs @@ -21,7 +21,8 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz CieXyz pcsTo = pcsFrom.ToProfileConnectingSpace(options); // Adapt to target white point - pcsTo = VonKriesChromaticAdaptation.Transform(options, in pcsTo); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsTo = VonKriesChromaticAdaptation.Transform(in pcsTo, whitePoints, options.AdaptationMatrix); // Convert to output from PCS return TTo.FromProfileConnectingSpace(options, pcsTo); @@ -44,7 +45,8 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz CieLab.ToProfileConnectionSpace(options, pcsFrom, pcsTo); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsTo, pcsTo); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsTo, pcsTo, whitePoints, options.AdaptationMatrix); // Convert to output from PCS TTo.FromProfileConnectionSpace(options, pcsTo, destination); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs index bbedb2e2b..89e03f3ae 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs @@ -19,7 +19,8 @@ internal static class ColorProfileConverterExtensionsCieLabRgb CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); // Adapt to target white point - pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix); // Convert between PCS Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB); @@ -44,7 +45,8 @@ internal static class ColorProfileConverterExtensionsCieLabRgb CieLab.ToProfileConnectionSpace(options, pcsFromA, pcsFromB); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFromB, pcsFromB); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsFromB, pcsFromB, whitePoints, options.AdaptationMatrix); // Convert between PCS. using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs index a73677129..de44cb17e 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs @@ -18,7 +18,8 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab CieXyz pcsFrom = source.ToProfileConnectingSpace(options); // Adapt to target white point - pcsFrom = VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsFrom = VonKriesChromaticAdaptation.Transform(in pcsFrom, whitePoints, options.AdaptationMatrix); // Convert between PCS CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFrom); @@ -39,7 +40,8 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab TFrom.ToProfileConnectionSpace(options, source, pcsFrom); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix); // Convert between PCS. using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs index fd6ff21e4..cea7ea73a 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs @@ -18,7 +18,8 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz CieXyz pcsFrom = source.ToProfileConnectingSpace(options); // Adapt to target white point - pcsFrom = VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsFrom = VonKriesChromaticAdaptation.Transform(in pcsFrom, whitePoints, options.AdaptationMatrix); // Convert to output from PCS return TTo.FromProfileConnectingSpace(options, pcsFrom); @@ -36,7 +37,8 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz TFrom.ToProfileConnectionSpace(options, source, pcsFrom); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix); // Convert to output from PCS TTo.FromProfileConnectionSpace(options, pcsFrom, destination); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs index b0ec427c5..0d267e56f 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Buffers; @@ -18,7 +18,8 @@ internal static class ColorProfileConverterExtensionsCieXyzRgb CieXyz pcsFrom = source.ToProfileConnectingSpace(options); // Adapt to target white point - pcsFrom = VonKriesChromaticAdaptation.Transform(options, in pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsFrom = VonKriesChromaticAdaptation.Transform(in pcsFrom, whitePoints, options.AdaptationMatrix); // Convert between PCS Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFrom); @@ -39,7 +40,8 @@ internal static class ColorProfileConverterExtensionsCieXyzRgb TFrom.ToProfileConnectionSpace(options, source, pcsFrom); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix); // Convert between PCS. using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs index baa92755c..50503bb07 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs @@ -19,7 +19,8 @@ internal static class ColorProfileConverterExtensionsRgbCieLab CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); // Adapt to target white point - pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix); // Convert between PCS CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB); @@ -44,7 +45,8 @@ internal static class ColorProfileConverterExtensionsRgbCieLab Rgb.ToProfileConnectionSpace(options, pcsFromA, pcsFromB); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFromB, pcsFromB); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsFromB, pcsFromB, whitePoints, options.AdaptationMatrix); // Convert between PCS. using IMemoryOwner pcsToOwner = options.MemoryAllocator.Allocate(source.Length); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs index f98d4bfc3..19d04e953 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Buffers; @@ -21,7 +21,8 @@ internal static class ColorProfileConverterExtensionsRgbCieXyz CieXyz pcsTo = pcsFrom.ToProfileConnectingSpace(options); // Adapt to target white point - pcsTo = VonKriesChromaticAdaptation.Transform(options, in pcsTo); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsTo = VonKriesChromaticAdaptation.Transform(in pcsTo, whitePoints, options.AdaptationMatrix); // Convert to output from PCS return TTo.FromProfileConnectingSpace(options, pcsTo); @@ -44,7 +45,8 @@ internal static class ColorProfileConverterExtensionsRgbCieXyz Rgb.ToProfileConnectionSpace(options, pcsFrom, pcsTo); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsTo, pcsTo); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsTo, pcsTo, whitePoints, options.AdaptationMatrix); // Convert to output from PCS TTo.FromProfileConnectionSpace(options, pcsTo, destination); diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs index 248a54026..f7edd4ba4 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs @@ -19,7 +19,8 @@ internal static class ColorProfileConverterExtensionsRgbRgb CieXyz pcsFromB = pcsFromA.ToProfileConnectingSpace(options); // Adapt to target white point - pcsFromB = VonKriesChromaticAdaptation.Transform(options, in pcsFromB); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + pcsFromB = VonKriesChromaticAdaptation.Transform(in pcsFromB, whitePoints, options.AdaptationMatrix); // Convert between PCS Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB); @@ -44,7 +45,8 @@ internal static class ColorProfileConverterExtensionsRgbRgb Rgb.ToProfileConnectionSpace(options, pcsFromTo, pcsFrom); // Adapt to target white point - VonKriesChromaticAdaptation.Transform(options, pcsFrom, pcsFrom); + (CieXyz From, CieXyz To) whitePoints = converter.GetChromaticAdaptionWhitePoints(); + VonKriesChromaticAdaptation.Transform(pcsFrom, pcsFrom, whitePoints, options.AdaptationMatrix); // Convert between PCS. Rgb.FromProfileConnectionSpace(options, pcsFrom, pcsFromTo); diff --git a/src/ImageSharp/ColorProfiles/HunterLab.cs b/src/ImageSharp/ColorProfiles/HunterLab.cs index 97a9004f7..5d6c01a2b 100644 --- a/src/ImageSharp/ColorProfiles/HunterLab.cs +++ b/src/ImageSharp/ColorProfiles/HunterLab.cs @@ -181,7 +181,7 @@ public readonly struct HunterLab : IColorProfile [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float ComputeKa(in CieXyz whitePoint) { - if (whitePoint.Equals(Illuminants.C)) + if (whitePoint.Equals(KnownIlluminants.C)) { return 175F; } @@ -192,7 +192,7 @@ public readonly struct HunterLab : IColorProfile [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float ComputeKb(in CieXyz whitePoint) { - if (whitePoint == Illuminants.C) + if (whitePoint == KnownIlluminants.C) { return 70F; } diff --git a/src/ImageSharp/ColorProfiles/Illuminants.cs b/src/ImageSharp/ColorProfiles/KnownIlluminants.cs similarity index 96% rename from src/ImageSharp/ColorProfiles/Illuminants.cs rename to src/ImageSharp/ColorProfiles/KnownIlluminants.cs index 0295dc484..ac5cbea2c 100644 --- a/src/ImageSharp/ColorProfiles/Illuminants.cs +++ b/src/ImageSharp/ColorProfiles/KnownIlluminants.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.ColorProfiles; @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; ///
/// Descriptions taken from: http://en.wikipedia.org/wiki/Standard_illuminant /// -public static class Illuminants +public static class KnownIlluminants { /// /// Incandescent / Tungsten diff --git a/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs b/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs index 3b15c7fe6..dfb1ec631 100644 --- a/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs +++ b/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs @@ -19,96 +19,96 @@ public static class KnownRgbWorkingSpaces /// Uses proper companding function, according to: /// /// - public static readonly RgbWorkingSpace SRgb = new SRgbWorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace SRgb = new SRgbWorkingSpace(KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Simplified sRgb working space (uses gamma companding instead of ). /// See also . /// - public static readonly RgbWorkingSpace SRgbSimplified = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace SRgbSimplified = new GammaWorkingSpace(2.2F, KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Rec. 709 (ITU-R Recommendation BT.709) working space. /// - public static readonly RgbWorkingSpace Rec709 = new Rec709WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); + public static readonly RgbWorkingSpace Rec709 = new Rec709WorkingSpace(KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); /// /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. /// - public static readonly RgbWorkingSpace Rec2020 = new Rec2020WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); + public static readonly RgbWorkingSpace Rec2020 = new Rec2020WorkingSpace(KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); /// /// ECI Rgb v2 working space. /// - public static readonly RgbWorkingSpace ECIRgbv2 = new LWorkingSpace(Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + public static readonly RgbWorkingSpace ECIRgbv2 = new LWorkingSpace(KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); /// /// Adobe Rgb (1998) working space. /// - public static readonly RgbWorkingSpace AdobeRgb1998 = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace AdobeRgb1998 = new GammaWorkingSpace(2.2F, KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// Apple sRgb working space. /// - public static readonly RgbWorkingSpace ApplesRgb = new GammaWorkingSpace(1.8F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + public static readonly RgbWorkingSpace ApplesRgb = new GammaWorkingSpace(1.8F, KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); /// /// Best Rgb working space. /// - public static readonly RgbWorkingSpace BestRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + public static readonly RgbWorkingSpace BestRgb = new GammaWorkingSpace(2.2F, KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); /// /// Beta Rgb working space. /// - public static readonly RgbWorkingSpace BetaRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); + public static readonly RgbWorkingSpace BetaRgb = new GammaWorkingSpace(2.2F, KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); /// /// Bruce Rgb working space. /// - public static readonly RgbWorkingSpace BruceRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace BruceRgb = new GammaWorkingSpace(2.2F, KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// CIE Rgb working space. /// - public static readonly RgbWorkingSpace CIERgb = new GammaWorkingSpace(2.2F, Illuminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); + public static readonly RgbWorkingSpace CIERgb = new GammaWorkingSpace(2.2F, KnownIlluminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); /// /// ColorMatch Rgb working space. /// - public static readonly RgbWorkingSpace ColorMatchRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); + public static readonly RgbWorkingSpace ColorMatchRgb = new GammaWorkingSpace(1.8F, KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); /// /// Don Rgb 4 working space. /// - public static readonly RgbWorkingSpace DonRgb4 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); + public static readonly RgbWorkingSpace DonRgb4 = new GammaWorkingSpace(2.2F, KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); /// /// Ekta Space PS5 working space. /// - public static readonly RgbWorkingSpace EktaSpacePS5 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); + public static readonly RgbWorkingSpace EktaSpacePS5 = new GammaWorkingSpace(2.2F, KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); /// /// NTSC Rgb working space. /// - public static readonly RgbWorkingSpace NTSCRgb = new GammaWorkingSpace(2.2F, Illuminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); + public static readonly RgbWorkingSpace NTSCRgb = new GammaWorkingSpace(2.2F, KnownIlluminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); /// /// PAL/SECAM Rgb working space. /// - public static readonly RgbWorkingSpace PALSECAMRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); + public static readonly RgbWorkingSpace PALSECAMRgb = new GammaWorkingSpace(2.2F, KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); /// /// ProPhoto Rgb working space. /// - public static readonly RgbWorkingSpace ProPhotoRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); + public static readonly RgbWorkingSpace ProPhotoRgb = new GammaWorkingSpace(1.8F, KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); /// /// SMPTE-C Rgb working space. /// - public static readonly RgbWorkingSpace SMPTECRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); + public static readonly RgbWorkingSpace SMPTECRgb = new GammaWorkingSpace(2.2F, KnownIlluminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); /// /// Wide Gamut Rgb working space. /// - public static readonly RgbWorkingSpace WideGamutRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); + public static readonly RgbWorkingSpace WideGamutRgb = new GammaWorkingSpace(2.2F, KnownIlluminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); } diff --git a/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs index 4719750b7..2f9a52912 100644 --- a/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs +++ b/src/ImageSharp/ColorProfiles/VonKriesChromaticAdaptation.cs @@ -19,34 +19,24 @@ public static class VonKriesChromaticAdaptation /// /// Performs a linear transformation of a source color in to the destination color. /// - /// The type of color profile to convert from. - /// The type of color profile to convert to. /// Doesn't crop the resulting color space coordinates (e.g. allows negative values for XYZ coordinates). - /// The color profile conversion options. /// The source color. + /// The conversion white points. + /// The chromatic adaptation matrix. /// The - public static CieXyz Transform(ColorConversionOptions options, in CieXyz source) - where TFrom : struct, IColorProfile - where TTo : struct, IColorProfile + public static CieXyz Transform(in CieXyz source, (CieXyz From, CieXyz To) whitePoints, Matrix4x4 matrix) { - CieXyz sourceWhitePoint = TFrom.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint - ? options.WhitePoint - : options.RgbWorkingSpace.WhitePoint; + CieXyz from = whitePoints.From; + CieXyz to = whitePoints.To; - CieXyz targetWhitePoint = TTo.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint - ? options.TargetWhitePoint - : options.TargetRgbWorkingSpace.WhitePoint; - - if (sourceWhitePoint.Equals(targetWhitePoint)) + if (from.Equals(to)) { return new(source.X, source.Y, source.Z); } - Matrix4x4 matrix = options.AdaptationMatrix; - Vector3 sourceColorLms = Vector3.Transform(source.ToVector3(), matrix); - Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix); - Vector3 targetWhitePointLms = Vector3.Transform(targetWhitePoint.ToVector3(), matrix); + Vector3 sourceWhitePointLms = Vector3.Transform(from.ToVector3(), matrix); + Vector3 targetWhitePointLms = Vector3.Transform(to.ToVector3(), matrix); Vector3 vector = targetWhitePointLms / sourceWhitePointLms; Vector3 targetColorLms = Vector3.Multiply(vector, sourceColorLms); @@ -58,41 +48,36 @@ public static class VonKriesChromaticAdaptation /// /// Performs a bulk linear transformation of a source color in to the destination color. /// - /// The type of color profile to convert from. - /// The type of color profile to convert to. /// Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates). - /// The color profile conversion options. /// The span to the source colors. /// The span to the destination colors. - public static void Transform(ColorConversionOptions options, ReadOnlySpan source, Span destination) - where TFrom : struct, IColorProfile - where TTo : struct, IColorProfile + /// The conversion white points. + /// The chromatic adaptation matrix. + public static void Transform( + ReadOnlySpan source, + Span destination, + (CieXyz From, CieXyz To) whitePoints, + Matrix4x4 matrix) { Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); int count = source.Length; - CieXyz sourceWhitePoint = TFrom.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint - ? options.WhitePoint - : options.RgbWorkingSpace.WhitePoint; - - CieXyz targetWhitePoint = TTo.GetChromaticAdaptionWhitePointSource() == ChromaticAdaptionWhitePointSource.WhitePoint - ? options.TargetWhitePoint - : options.TargetRgbWorkingSpace.WhitePoint; + CieXyz from = whitePoints.From; + CieXyz to = whitePoints.To; - if (sourceWhitePoint.Equals(targetWhitePoint)) + if (from.Equals(to)) { source.CopyTo(destination[..count]); return; } - Matrix4x4 matrix = options.AdaptationMatrix; Matrix4x4.Invert(matrix, out Matrix4x4 inverseMatrix); ref CieXyz sourceBase = ref MemoryMarshal.GetReference(source); ref CieXyz destinationBase = ref MemoryMarshal.GetReference(destination); - Vector3 sourceWhitePointLms = Vector3.Transform(sourceWhitePoint.ToVector3(), matrix); - Vector3 targetWhitePointLms = Vector3.Transform(targetWhitePoint.ToVector3(), matrix); + Vector3 sourceWhitePointLms = Vector3.Transform(from.ToVector3(), matrix); + Vector3 targetWhitePointLms = Vector3.Transform(to.ToVector3(), matrix); Vector3 vector = targetWhitePointLms / sourceWhitePointLms; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs index 0eaf30b81..9a894c776 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchConversionTests.cs @@ -30,7 +30,7 @@ public class CieLabAndCieLchConversionTests // Arrange CieLch input = new(l, c, h); CieLab expected = new(l2, a, b); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D50 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D50 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLch[5]; @@ -65,7 +65,7 @@ public class CieLabAndCieLchConversionTests // Arrange CieLab input = new(l, a, b); CieLch expected = new(l2, c, h); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D50 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D50 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLab[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs index 13016fe87..4b1b5e1a5 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLchuvConversionTests.cs @@ -24,7 +24,7 @@ public class CieLabAndCieLchuvConversionTests // Arrange CieLchuv input = new(l, c, h); CieLab expected = new(l2, a, b); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLchuv[5]; @@ -53,7 +53,7 @@ public class CieLabAndCieLchuvConversionTests // Arrange CieLab input = new(l, a, b); CieLchuv expected = new(l2, c, h); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLab[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs index cd1d08f5d..44756c779 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabAndCieLuvConversionTests.cs @@ -24,7 +24,7 @@ public class CieLabAndCieLuvConversionTests // Arrange CieLuv input = new(l, u, v); CieLab expected = new(l2, a, b); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -53,7 +53,7 @@ public class CieLabAndCieLuvConversionTests // Arrange CieLab input = new(l, a, b); CieLuv expected = new(l2, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLab[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs index cb0e587be..598d4af33 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchAndCieLuvConversionTests.cs @@ -20,7 +20,7 @@ public class CieLchAndCieLuvConversionTests // Arrange CieLch input = new(l, c, h); CieLuv expected = new(l2, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLch[5]; @@ -48,7 +48,7 @@ public class CieLchAndCieLuvConversionTests // Arrange CieLuv input = new(l2, u, v); CieLch expected = new(l, c, h); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs index 5b94572f6..a3e0b45e0 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLchConversionTests.cs @@ -20,7 +20,7 @@ public class CieLchuvAndCieLchConversionTests // Arrange CieLch input = new(l2, c2, h2); CieLchuv expected = new(l, c, h); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLch[5]; @@ -48,7 +48,7 @@ public class CieLchuvAndCieLchConversionTests // Arrange CieLchuv input = new(l, c, h); CieLch expected = new(l2, c2, h2); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLchuv[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs index 43fd62355..465237490 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCieLuvConversionTests.cs @@ -30,7 +30,7 @@ public class CieLchuvAndCieLuvConversionTests // Arrange CieLchuv input = new(l, c, h); CieLuv expected = new(l2, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLchuv[5]; @@ -66,7 +66,7 @@ public class CieLchuvAndCieLuvConversionTests // Arrange CieLuv input = new(l, u, v); CieLchuv expected = new(l2, c, h); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs index 2fb97871e..60ac3da16 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvAndCmykConversionTests.cs @@ -20,7 +20,7 @@ public class CieLchuvAndCmykConversionTests // Arrange Cmyk input = new(c2, m, y, k); CieLchuv expected = new(l, c, h); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new Cmyk[5]; @@ -49,7 +49,7 @@ public class CieLchuvAndCmykConversionTests // Arrange CieLchuv input = new(l, c, h); Cmyk expected = new(c2, m, y, k); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLchuv[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs index edc342eaa..e73edcda7 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndCieXyyConversionTests.cs @@ -20,7 +20,7 @@ public class CieLuvAndCieXyyConversionTests // Arrange CieLuv input = new(l, u, v); CieXyy expected = new(x, y, yl); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -49,7 +49,7 @@ public class CieLuvAndCieXyyConversionTests // Arrange CieXyy input = new(x, y, yl); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieXyy[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs index c6fb40149..b178b22b2 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHslConversionTests.cs @@ -20,7 +20,7 @@ public class CieLuvAndHslConversionTests // Arrange CieLuv input = new(l, u, v); Hsl expected = new(h, s, l2); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -49,7 +49,7 @@ public class CieLuvAndHslConversionTests // Arrange Hsl input = new(h, s, l2); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new Hsl[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs index f736b9a28..286609337 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHsvConversionTests.cs @@ -20,7 +20,7 @@ public class CieLuvAndHsvConversionTests // Arrange CieLuv input = new(l, u, v); Hsv expected = new(h, s, v2); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -49,7 +49,7 @@ public class CieLuvAndHsvConversionTests // Arrange Hsv input = new(h, s, v2); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new Hsv[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs index 6f674f53e..ab44d2dca 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs @@ -20,7 +20,7 @@ public class CieLuvAndHunterLabConversionTests // Arrange CieLuv input = new(l, u, v); HunterLab expected = new(l2, a, b); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D50 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D50 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -49,7 +49,7 @@ public class CieLuvAndHunterLabConversionTests // Arrange HunterLab input = new(l2, a, b); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new HunterLab[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs index f2f5ada62..812ca44dd 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndLmsConversionTests.cs @@ -20,7 +20,7 @@ public class CieLuvAndLmsConversionTests // Arrange CieLuv input = new(l, u, v); Lms expected = new(l2, m, s); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -49,7 +49,7 @@ public class CieLuvAndLmsConversionTests // Arrange Lms input = new(l2, m, s); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new Lms[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs index b0106fc57..f1da6e33f 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndRgbConversionTests.cs @@ -20,7 +20,7 @@ public class CieLuvAndRgbConversionTests // Arrange CieLuv input = new(l, u, v); Rgb expected = new(r, g, b); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -49,7 +49,7 @@ public class CieLuvAndRgbConversionTests // Arrange Rgb input = new(r, g, b); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new Rgb[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs index 6fdb440d7..fa7e2ece3 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndYCbCrConversionTests.cs @@ -20,7 +20,7 @@ public class CieLuvAndYCbCrConversionTests // Arrange CieLuv input = new(l, u, v); YCbCr expected = new(y, cb, cr); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; @@ -49,7 +49,7 @@ public class CieLuvAndYCbCrConversionTests // Arrange YCbCr input = new(y, cb, cr); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new YCbCr[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs index a88798881..cb4d02889 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLabConversionTest.cs @@ -29,7 +29,7 @@ public class CieXyzAndCieLabConversionTest { // Arrange CieLab input = new(l, a, b); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); CieXyz expected = new(x, y, z); @@ -62,7 +62,7 @@ public class CieXyzAndCieLabConversionTest { // Arrange CieXyz input = new(x, y, z); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); CieLab expected = new(l, a, b); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs index 4a6c3c4f8..944b99005 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzAndCieLuvConversionTest.cs @@ -29,7 +29,7 @@ public class CieXyzAndCieLuvConversionTest CieXyz input = new(x, y, z); CieLuv expected = new(l, u, v); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieXyz[5]; @@ -64,7 +64,7 @@ public class CieXyzAndCieLuvConversionTest CieLuv input = new(l, u, v); CieXyz expected = new(x, y, z); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetWhitePoint = Illuminants.D65 }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetWhitePoint = KnownIlluminants.D65 }; ColorProfileConverter converter = new(options); Span inputSpan = new CieLuv[5]; diff --git a/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs b/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs new file mode 100644 index 000000000..947f153c2 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs @@ -0,0 +1,204 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ColorProfiles; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests chromatic adaptation within the . +/// Test data generated using: +/// +/// +/// +public class ColorProfileConverterChomaticAdaptationTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001F); + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 1, 1, 1, 1, 1)] + [InlineData(0.206162, 0.260277, 0.746717, 0.220000, 0.130000, 0.780000)] + public void Adapt_RGB_WideGamutRGB_To_sRGB(float r1, float g1, float b1, float r2, float g2, float b2) + { + // Arrange + Rgb input = new(r1, g1, b1); + Rgb expected = new(r2, g2, b2); + ColorConversionOptions options = new() + { + RgbWorkingSpace = KnownRgbWorkingSpaces.WideGamutRgb, + TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb + }; + ColorProfileConverter converter = new(options); + + // Action + Rgb actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(1, 1, 1, 1, 1, 1)] + [InlineData(0.220000, 0.130000, 0.780000, 0.206162, 0.260277, 0.746717)] + public void Adapt_RGB_SRGB_To_WideGamutRGB(float r1, float g1, float b1, float r2, float g2, float b2) + { + // Arrange + Rgb input = new(r1, g1, b1); + Rgb expected = new(r2, g2, b2); + ColorConversionOptions options = new() + { + RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb, + TargetRgbWorkingSpace = KnownRgbWorkingSpaces.WideGamutRgb + }; + ColorProfileConverter converter = new(options); + + // Action + Rgb actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(22, 33, 1, 22.269869, 32.841164, 1.633926)] + public void Adapt_Lab_D65_To_D50(float l1, float a1, float b1, float l2, float a2, float b2) + { + // Arrange + CieLab input = new(l1, a1, b1); + CieLab expected = new(l2, a2, b2); + ColorConversionOptions options = new() + { + WhitePoint = KnownIlluminants.D65, + TargetWhitePoint = KnownIlluminants.D50 + }; + ColorProfileConverter converter = new(options); + + // Action + CieLab actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.5, 0.5, 0.5, 0.510286, 0.501489, 0.378970)] + public void Adapt_Xyz_D65_To_D50_Bradford(float x1, float y1, float z1, float x2, float y2, float z2) + { + // Arrange + CieXyz input = new(x1, y1, z1); + CieXyz expected = new(x2, y2, z2); + ColorConversionOptions options = new() + { + WhitePoint = KnownIlluminants.D65, + TargetWhitePoint = KnownIlluminants.D50, + AdaptationMatrix = KnownChromaticAdaptationMatrices.Bradford + }; + + ColorProfileConverter converter = new(options); + + // Action + CieXyz actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(0.5, 0.5, 0.5, 0.507233, 0.500000, 0.378943)] + public void Adapt_Xyz_D65_To_D50_XyzScaling(float x1, float y1, float z1, float x2, float y2, float z2) + { + // Arrange + CieXyz input = new(x1, y1, z1); + CieXyz expected = new(x2, y2, z2); + ColorConversionOptions options = new() + { + WhitePoint = KnownIlluminants.D65, + TargetWhitePoint = KnownIlluminants.D50, + AdaptationMatrix = KnownChromaticAdaptationMatrices.XyzScaling + }; + + ColorProfileConverter converter = new(options); + + // Action + CieXyz actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(22, 33, 1, 22.28086, 33.0681534, 1.30099022)] + public void Adapt_HunterLab_D65_To_D50(float l1, float a1, float b1, float l2, float a2, float b2) + { + // Arrange + HunterLab input = new(l1, a1, b1); + HunterLab expected = new(l2, a2, b2); + ColorConversionOptions options = new() + { + WhitePoint = KnownIlluminants.D65, + TargetWhitePoint = KnownIlluminants.D50, + }; + + ColorProfileConverter converter = new(options); + + // Action + HunterLab actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } + + [Theory] + [InlineData(0, 0, 0, 0, 0, 0)] + [InlineData(22, 33, 1, 22, 34.0843468, 359.009)] + public void Adapt_CieLchuv_D65_To_D50_XyzScaling(float l1, float c1, float h1, float l2, float c2, float h2) + { + // Arrange + CieLchuv input = new(l1, c1, h1); + CieLchuv expected = new(l2, c2, h2); + ColorConversionOptions options = new() + { + WhitePoint = KnownIlluminants.D65, + TargetWhitePoint = KnownIlluminants.D50, + AdaptationMatrix = KnownChromaticAdaptationMatrices.XyzScaling + }; + + ColorProfileConverter converter = new(options); + + // Action + CieLchuv actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } + + [Theory] + [InlineData(22, 33, 1, 22, 33, 0.9999999)] + public void Adapt_CieLch_D65_To_D50_XyzScaling(float l1, float c1, float h1, float l2, float c2, float h2) + { + // Arrange + CieLch input = new(l1, c1, h1); + CieLch expected = new(l2, c2, h2); + ColorConversionOptions options = new() + { + WhitePoint = KnownIlluminants.D65, + TargetWhitePoint = KnownIlluminants.D50, + AdaptationMatrix = KnownChromaticAdaptationMatrices.XyzScaling + }; + + ColorProfileConverter converter = new(options); + + // Action + CieLch actual = converter.Convert(input); + + // Assert + Assert.Equal(expected, actual, Comparer); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs index 31f8a9478..960dc50fa 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs @@ -28,7 +28,7 @@ public class RgbAndCieXyzConversionTest { // Arrange CieXyz input = new(x, y, z); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D50, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D50, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); Rgb expected = new(r, g, b); @@ -61,7 +61,7 @@ public class RgbAndCieXyzConversionTest { // Arrange CieXyz input = new(x, y, z); - ColorConversionOptions options = new() { WhitePoint = Illuminants.D65, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { WhitePoint = KnownIlluminants.D65, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); Rgb expected = new(r, g, b); @@ -94,7 +94,7 @@ public class RgbAndCieXyzConversionTest { // Arrange Rgb input = new(r, g, b); - ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D50, RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { TargetWhitePoint = KnownIlluminants.D50, RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); CieXyz expected = new(x, y, z); @@ -127,7 +127,7 @@ public class RgbAndCieXyzConversionTest { // Arrange Rgb input = new(r, g, b); - ColorConversionOptions options = new() { TargetWhitePoint = Illuminants.D65, RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; + ColorConversionOptions options = new() { TargetWhitePoint = KnownIlluminants.D65, RgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }; ColorProfileConverter converter = new(options); CieXyz expected = new(x, y, z); diff --git a/tests/ImageSharp.Tests/ColorProfiles/VonKriesChromaticAdaptationTests.cs b/tests/ImageSharp.Tests/ColorProfiles/VonKriesChromaticAdaptationTests.cs new file mode 100644 index 000000000..7f5687dee --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/VonKriesChromaticAdaptationTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +public class VonKriesChromaticAdaptationTests +{ + private static readonly ApproximateColorProfileComparer Comparer = new(.0001F); + public static readonly TheoryData WhitePoints = new() + { + { KnownIlluminants.D65, KnownIlluminants.D50 }, + { KnownIlluminants.D65, KnownIlluminants.D65 } + }; + + [Theory] + [MemberData(nameof(WhitePoints))] + public void SingleAndBulkTransformYieldIdenticalResults(CieXyz from, CieXyz to) + { + ColorConversionOptions options = new() + { + WhitePoint = from, + TargetWhitePoint = to + }; + + CieXyz input = new(1, 0, 1); + CieXyz expected = VonKriesChromaticAdaptation.Transform(in input, (from, to), KnownChromaticAdaptationMatrices.Bradford); + + Span inputSpan = new CieXyz[5]; + inputSpan.Fill(input); + + Span actualSpan = new CieXyz[5]; + + VonKriesChromaticAdaptation.Transform(inputSpan, actualSpan, (from, to), KnownChromaticAdaptationMatrices.Bradford); + + for (int i = 0; i < inputSpan.Length; i++) + { + Assert.Equal(expected, actualSpan[i], Comparer); + } + } +} From 72dbc1308c6b7eb94202b1969c9f5f3fa1ad6898 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 16 May 2024 23:12:19 +1000 Subject: [PATCH 38/79] Complete conversion tests --- src/ImageSharp/ColorProfiles/CieLab.cs | 6 +- src/ImageSharp/ColorProfiles/CieLuv.cs | 14 +++ src/ImageSharp/ColorProfiles/CieXyz.cs | 7 +- src/ImageSharp/ColorProfiles/Rgb.cs | 39 +++++-- .../CieLuvAndHunterLabConversionTests.cs | 4 +- .../ColorProfiles/CompandingTests.cs | 108 ++++++++++++++++++ .../ColorProfiles/RgbAndHslConversionTest.cs | 2 +- .../StringRepresentationTests.cs | 60 ++++++++++ 8 files changed, 224 insertions(+), 16 deletions(-) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CompandingTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/StringRepresentationTests.cs diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs index d7dd7415d..72148af45 100644 --- a/src/ImageSharp/ColorProfiles/CieLab.cs +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -26,8 +26,11 @@ public readonly struct CieLab : IProfileConnectingSpace /// The b (blue - yellow) component. [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieLab(float l, float a, float b) - : this(new Vector3(l, a, b)) { + // Not clamping as documentation about this space only indicates "usual" ranges + this.L = l; + this.A = a; + this.B = b; } /// @@ -38,7 +41,6 @@ public readonly struct CieLab : IProfileConnectingSpace public CieLab(Vector3 vector) : this() { - // Not clamping as documentation about this space only indicates "usual" ranges this.L = vector.X; this.A = vector.Y; this.B = vector.Z; diff --git a/src/ImageSharp/ColorProfiles/CieLuv.cs b/src/ImageSharp/ColorProfiles/CieLuv.cs index 64541e496..9bf102057 100644 --- a/src/ImageSharp/ColorProfiles/CieLuv.cs +++ b/src/ImageSharp/ColorProfiles/CieLuv.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -28,6 +29,19 @@ public readonly struct CieLuv : IColorProfile this.V = v; } + /// + /// Initializes a new instance of the struct. + /// + /// The vector representing the l, u, v components. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public CieLuv(Vector3 vector) + : this() + { + this.L = vector.X; + this.U = vector.Y; + this.V = vector.Z; + } + /// /// Gets the lightness dimension /// A value usually ranging between 0 and 100. diff --git a/src/ImageSharp/ColorProfiles/CieXyz.cs b/src/ImageSharp/ColorProfiles/CieXyz.cs index ec28a232d..fbd3e77d7 100644 --- a/src/ImageSharp/ColorProfiles/CieXyz.cs +++ b/src/ImageSharp/ColorProfiles/CieXyz.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.ColorProfiles; @@ -20,8 +21,11 @@ public readonly struct CieXyz : IProfileConnectingSpace /// Z is quasi-equal to blue stimulation, or the S cone of the human eye. [MethodImpl(MethodImplOptions.AggressiveInlining)] public CieXyz(float x, float y, float z) - : this(new Vector3(x, y, z)) { + // Not clamping as documentation about this space only indicates "usual" ranges + this.X = x; + this.Y = y; + this.Z = z; } /// @@ -31,7 +35,6 @@ public readonly struct CieXyz : IProfileConnectingSpace public CieXyz(Vector3 vector) : this() { - // Not clamping as documentation about this space only indicates "usual" ranges this.X = vector.X; this.Y = vector.Y; this.Z = vector.Z; diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs index 21575dd85..697c0fbd8 100644 --- a/src/ImageSharp/ColorProfiles/Rgb.cs +++ b/src/ImageSharp/ColorProfiles/Rgb.cs @@ -21,6 +21,7 @@ public readonly struct Rgb : IProfileConnectingSpace [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rgb(float r, float g, float b) { + // Not clamping as this space can exceed "usual" ranges this.R = r; this.G = g; this.B = b; @@ -31,7 +32,7 @@ public readonly struct Rgb : IProfileConnectingSpace /// /// The vector representing the r, g, b components. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Rgb(Vector3 source) + public Rgb(Vector3 source) { this.R = source.X; this.G = source.Y; @@ -149,20 +150,32 @@ public readonly struct Rgb : IProfileConnectingSpace public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.RgbWorkingSpace; /// - /// Initializes the pixel instance from a generic scaled . + /// Initializes the color instance from a generic scaled . /// - /// The vector to load the pixel from. + /// The vector to load the color from. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgb FromScaledVector3(Vector3 source) => new(source); + public static Rgb FromScaledVector3(Vector3 source) => new(Vector3.Clamp(source, Vector3.Zero, Vector3.One)); /// - /// Initializes the pixel instance from a generic scaled . + /// Initializes the color instance from a generic scaled . /// - /// The vector to load the pixel from. + /// The vector to load the color from. /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Rgb FromScaledVector4(Vector4 source) => new(source.X, source.Y, source.Z); + public static Rgb FromScaledVector4(Vector4 source) + { + source = Vector4.Clamp(source, Vector4.Zero, Vector4.One); + return new(source.X, source.Y, source.Z); + } + + /// + /// Initializes the color instance for a source clamped between 0 and 1 + /// + /// The source to load the color from. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Rgb Clamp(Rgb source) => new(Vector3.Clamp(new(source.R, source.G, source.B), Vector3.Zero, Vector3.One)); /// /// Expands the color into a generic ("scaled") representation @@ -171,7 +184,15 @@ public readonly struct Rgb : IProfileConnectingSpace /// /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector3 ToScaledVector3() => new(this.R, this.G, this.B); + public Vector3 ToScaledVector3() => Clamp(this).ToVector3(); + + /// + /// Expands the color into a generic representation. + /// The vector components are typically expanded in least to greatest significance order. + /// + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vector3 ToVector3() => new(this.R, this.G, this.B); /// /// Expands the color into a generic ("scaled") representation @@ -180,7 +201,7 @@ public readonly struct Rgb : IProfileConnectingSpace /// /// The . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ToScaledVector4() => new(this.R, this.G, this.B, 1f); + public Vector4 ToScaledVector4() => new(this.ToScaledVector3(), 1f); private static Matrix4x4 GetCieXyzToRgbMatrix(RgbWorkingSpace workingSpace) { diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs index ab44d2dca..73b605fb6 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvAndHunterLabConversionTests.cs @@ -15,7 +15,7 @@ public class CieLuvAndHunterLabConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(36.0555, 93.6901, 10.01514, 30.59289, 48.55542, 9.80487)] - public void Convert_CieLuv_to_HunterLab(float l, float u, float v, float l2, float a, float b) + public void Convert_CieLuv_To_HunterLab(float l, float u, float v, float l2, float a, float b) { // Arrange CieLuv input = new(l, u, v); @@ -44,7 +44,7 @@ public class CieLuvAndHunterLabConversionTests [Theory] [InlineData(0, 0, 0, 0, 0, 0)] [InlineData(30.59289, 48.55542, 9.80487, 36.0555, 93.6901, 10.01514)] - public void Convert_HunterLab_to_CieLuv(float l2, float a, float b, float l, float u, float v) + public void Convert_HunterLab_To_CieLuv(float l2, float a, float b, float l, float u, float v) { // Arrange HunterLab input = new(l2, a, b); diff --git a/tests/ImageSharp.Tests/ColorProfiles/CompandingTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CompandingTests.cs new file mode 100644 index 000000000..1bdefa109 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CompandingTests.cs @@ -0,0 +1,108 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles.Companding; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests various companding algorithms. Expanded numbers are hand calculated from formulas online. +/// +public class CompandingTests +{ + private static readonly ApproximateFloatComparer Comparer = new(.000001F); + + [Fact] + public void Rec2020Companding_IsCorrect() + { + Vector4 input = new(.667F); + Vector4 e = Rec2020Companding.Expand(input); + Vector4 c = Rec2020Companding.Compress(e); + CompandingIsCorrectImpl(e, c, .44847462F, input); + } + + [Fact] + public void Rec709Companding_IsCorrect() + { + Vector4 input = new(.667F); + Vector4 e = Rec709Companding.Expand(input); + Vector4 c = Rec709Companding.Compress(e); + CompandingIsCorrectImpl(e, c, .4483577F, input); + } + + [Fact] + public void SRgbCompanding_IsCorrect() + { + Vector4 input = new(.667F); + Vector4 e = SRgbCompanding.Expand(input); + Vector4 c = SRgbCompanding.Compress(e); + CompandingIsCorrectImpl(e, c, .40242353F, input); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(30)] + public void SRgbCompanding_Expand_VectorSpan(int length) + { + Random rnd = new(42); + Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); + Vector4[] expected = new Vector4[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + expected[i] = SRgbCompanding.Expand(source[i]); + } + + SRgbCompanding.Expand(source); + + Assert.Equal(expected, source, Comparer); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(30)] + public void SRgbCompanding_Compress_VectorSpan(int length) + { + Random rnd = new(42); + Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); + Vector4[] expected = new Vector4[source.Length]; + + for (int i = 0; i < source.Length; i++) + { + expected[i] = SRgbCompanding.Compress(source[i]); + } + + SRgbCompanding.Compress(source); + + Assert.Equal(expected, source, Comparer); + } + + [Fact] + public void GammaCompanding_IsCorrect() + { + const double gamma = 2.2; + Vector4 input = new(.667F); + Vector4 e = GammaCompanding.Expand(input, gamma); + Vector4 c = GammaCompanding.Compress(e, gamma); + CompandingIsCorrectImpl(e, c, .41027668F, input); + } + + [Fact] + public void LCompanding_IsCorrect() + { + Vector4 input = new(.667F); + Vector4 e = LCompanding.Expand(input); + Vector4 c = LCompanding.Compress(e); + CompandingIsCorrectImpl(e, c, .36236193F, input); + } + + private static void CompandingIsCorrectImpl(Vector4 e, Vector4 c, float expanded, Vector4 compressed) + { + // W (alpha) is already the linear representation of the color. + Assert.Equal(new Vector4(expanded, expanded, expanded, e.W), e, Comparer); + Assert.Equal(compressed, c, Comparer); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs index b63b9ca84..0dc95628b 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndHslConversionTest.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.ColorProfiles; -namespace SixLabors.ImageSharp.Tests.ColorProfiles.Conversion; +namespace SixLabors.ImageSharp.Tests.ColorProfiles; /// /// Tests - conversions. diff --git a/tests/ImageSharp.Tests/ColorProfiles/StringRepresentationTests.cs b/tests/ImageSharp.Tests/ColorProfiles/StringRepresentationTests.cs new file mode 100644 index 000000000..770c987db --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/StringRepresentationTests.cs @@ -0,0 +1,60 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +public class StringRepresentationTests +{ + private static readonly Vector3 One = new(1); + private static readonly Vector3 Zero = new(0); + private static readonly Vector3 Random = new(42.4F, 94.5F, 83.4F); + + public static readonly TheoryData TestData = new() + { + { new CieLab(Zero), "CieLab(0, 0, 0)" }, + { new CieLch(Zero), "CieLch(0, 0, 0)" }, + { new CieLchuv(Zero), "CieLchuv(0, 0, 0)" }, + { new CieLuv(Zero), "CieLuv(0, 0, 0)" }, + { new CieXyz(Zero), "CieXyz(0, 0, 0)" }, + { new CieXyy(Zero), "CieXyy(0, 0, 0)" }, + { new HunterLab(Zero), "HunterLab(0, 0, 0)" }, + { new Lms(Zero), "Lms(0, 0, 0)" }, + { new Rgb(Zero), "Rgb(0, 0, 0)" }, + { new Hsl(Zero), "Hsl(0, 0, 0)" }, + { new Hsv(Zero), "Hsv(0, 0, 0)" }, + { new YCbCr(Zero), "YCbCr(0, 0, 0)" }, + { new CieLab(One), "CieLab(1, 1, 1)" }, + { new CieLch(One), "CieLch(1, 1, 1)" }, + { new CieLchuv(One), "CieLchuv(1, 1, 1)" }, + { new CieLuv(One), "CieLuv(1, 1, 1)" }, + { new CieXyz(One), "CieXyz(1, 1, 1)" }, + { new CieXyy(One), "CieXyy(1, 1, 1)" }, + { new HunterLab(One), "HunterLab(1, 1, 1)" }, + { new Lms(One), "Lms(1, 1, 1)" }, + { new Rgb(One), "Rgb(1, 1, 1)" }, + { new Hsl(One), "Hsl(1, 1, 1)" }, + { new Hsv(One), "Hsv(1, 1, 1)" }, + { new YCbCr(One), "YCbCr(1, 1, 1)" }, + { new CieXyChromaticityCoordinates(1, 1), "CieXyChromaticityCoordinates(1, 1)" }, + { new CieLab(Random), "CieLab(42.4, 94.5, 83.4)" }, + { new CieLch(Random), "CieLch(42.4, 94.5, 83.4)" }, + { new CieLchuv(Random), "CieLchuv(42.4, 94.5, 83.4)" }, + { new CieLuv(Random), "CieLuv(42.4, 94.5, 83.4)" }, + { new CieXyz(Random), "CieXyz(42.4, 94.5, 83.4)" }, + { new CieXyy(Random), "CieXyy(42.4, 94.5, 83.4)" }, + { new HunterLab(Random), "HunterLab(42.4, 94.5, 83.4)" }, + { new Lms(Random), "Lms(42.4, 94.5, 83.4)" }, + { new Rgb(Random), "Rgb(42.4, 94.5, 83.4)" }, + { Rgb.Clamp(new Rgb(Random)), "Rgb(1, 1, 1)" }, + { new Hsl(Random), "Hsl(42.4, 1, 1)" }, // clamping to 1 is expected + { new Hsv(Random), "Hsv(42.4, 1, 1)" }, // clamping to 1 is expected + { new YCbCr(Random), "YCbCr(42.4, 94.5, 83.4)" }, + }; + + [Theory] + [MemberData(nameof(TestData))] + public void StringRepresentationsAreCorrect(object color, string text) => Assert.Equal(text, color.ToString()); +} From c16582b764005fa5570cd8e00603cb9af47ea566 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 May 2024 09:38:47 +1000 Subject: [PATCH 39/79] Complete tests --- .../ColorProfiles/CieLabTests.cs | 44 ++++++++++ .../ColorProfiles/CieLchTests.cs | 42 ++++++++++ .../ColorProfiles/CieLchuvTests.cs | 42 ++++++++++ .../ColorProfiles/CieLuvTests.cs | 42 ++++++++++ .../CieXyChromaticityCoordinatesTests.cs | 41 ++++++++++ .../ColorProfiles/CieXyyTests.cs | 42 ++++++++++ .../ColorProfiles/CieXyzTests.cs | 42 ++++++++++ .../ColorProfiles/CmykTests.cs | 44 ++++++++++ .../ColorProfiles/HslTests.cs | 42 ++++++++++ .../ColorProfiles/HsvTests.cs | 42 ++++++++++ .../ColorProfiles/HunterLabTests.cs | 43 ++++++++++ .../ColorProfiles/LmsTests.cs | 43 ++++++++++ .../ColorProfiles/RgbTests.cs | 81 +++++++++++++++++++ .../ColorProfiles/YCbCrTests.cs | 42 ++++++++++ 14 files changed, 632 insertions(+) create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLabTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLchTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLchuvTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieLuvTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyChromaticityCoordinatesTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyyTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CieXyzTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/CmykTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/HslTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/HsvTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/HunterLabTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/LmsTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/RgbTests.cs create mode 100644 tests/ImageSharp.Tests/ColorProfiles/YCbCrTests.cs diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLabTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLabTests.cs new file mode 100644 index 000000000..3c015259b --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLabTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CieLabTests +{ + [Fact] + public void CieLabConstructorAssignsFields() + { + const float l = 75F; + const float a = -64F; + const float b = 87F; + CieLab cieLab = new(l, a, b); + + Assert.Equal(l, cieLab.L); + Assert.Equal(a, cieLab.A); + Assert.Equal(b, cieLab.B); + } + + [Fact] + public void CieLabEquality() + { + CieLab x = default; + CieLab y = new(Vector3.One); + + Assert.True(default == default(CieLab)); + Assert.True(new CieLab(1, 0, 1) != default); + Assert.False(new CieLab(1, 0, 1) == default); + Assert.Equal(default, default(CieLab)); + Assert.Equal(new CieLab(1, 0, 1), new CieLab(1, 0, 1)); + Assert.Equal(new CieLab(Vector3.One), new CieLab(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(new CieLab(1, 0, 1) == default); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchTests.cs new file mode 100644 index 000000000..484db3e8c --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CieLchTests +{ + [Fact] + public void CieLchConstructorAssignsFields() + { + const float l = 75F; + const float c = 64F; + const float h = 287F; + CieLch cieLch = new(l, c, h); + + Assert.Equal(l, cieLch.L); + Assert.Equal(c, cieLch.C); + Assert.Equal(h, cieLch.H); + } + + [Fact] + public void CieLchEquality() + { + CieLch x = default; + CieLch y = new(Vector3.One); + + Assert.True(default == default(CieLch)); + Assert.False(default != default(CieLch)); + Assert.Equal(default, default(CieLch)); + Assert.Equal(new CieLch(1, 0, 1), new CieLch(1, 0, 1)); + Assert.Equal(new CieLch(Vector3.One), new CieLch(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLchuvTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvTests.cs new file mode 100644 index 000000000..0b737cdfc --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLchuvTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CieLchuvTests +{ + [Fact] + public void CieLchuvConstructorAssignsFields() + { + const float l = 75F; + const float c = 64F; + const float h = 287F; + CieLchuv cieLchuv = new(l, c, h); + + Assert.Equal(l, cieLchuv.L); + Assert.Equal(c, cieLchuv.C); + Assert.Equal(h, cieLchuv.H); + } + + [Fact] + public void CieLchuvEquality() + { + CieLchuv x = default; + CieLchuv y = new(Vector3.One); + + Assert.True(default == default(CieLchuv)); + Assert.False(default != default(CieLchuv)); + Assert.Equal(default, default(CieLchuv)); + Assert.Equal(new CieLchuv(1, 0, 1), new CieLchuv(1, 0, 1)); + Assert.Equal(new CieLchuv(Vector3.One), new CieLchuv(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieLuvTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieLuvTests.cs new file mode 100644 index 000000000..db903a0bf --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieLuvTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CieLuvTests +{ + [Fact] + public void CieLuvConstructorAssignsFields() + { + const float l = 75F; + const float c = -64F; + const float h = 87F; + CieLuv cieLuv = new(l, c, h); + + Assert.Equal(l, cieLuv.L); + Assert.Equal(c, cieLuv.U); + Assert.Equal(h, cieLuv.V); + } + + [Fact] + public void CieLuvEquality() + { + CieLuv x = default; + CieLuv y = new(Vector3.One); + + Assert.True(default == default(CieLuv)); + Assert.False(default != default(CieLuv)); + Assert.Equal(default, default(CieLuv)); + Assert.Equal(new CieLuv(1, 0, 1), new CieLuv(1, 0, 1)); + Assert.Equal(new CieLuv(Vector3.One), new CieLuv(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyChromaticityCoordinatesTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyChromaticityCoordinatesTests.cs new file mode 100644 index 000000000..a85a08a21 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyChromaticityCoordinatesTests.cs @@ -0,0 +1,41 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CieXyChromaticityCoordinatesTests +{ + [Fact] + public void CieXyChromaticityCoordinatesConstructorAssignsFields() + { + const float x = .75F; + const float y = .64F; + CieXyChromaticityCoordinates coordinates = new(x, y); + + Assert.Equal(x, coordinates.X); + Assert.Equal(y, coordinates.Y); + } + + [Fact] + public void CieXyChromaticityCoordinatesEquality() + { + CieXyChromaticityCoordinates x = default; + CieXyChromaticityCoordinates y = new(1, 1); + + Assert.True(default == default(CieXyChromaticityCoordinates)); + Assert.True(new CieXyChromaticityCoordinates(1, 0) != default); + Assert.False(new CieXyChromaticityCoordinates(1, 0) == default); + Assert.Equal(default, default(CieXyChromaticityCoordinates)); + Assert.Equal(new CieXyChromaticityCoordinates(1, 0), new CieXyChromaticityCoordinates(1, 0)); + Assert.Equal(new CieXyChromaticityCoordinates(1, 1), new CieXyChromaticityCoordinates(1, 1)); + Assert.False(x.Equals(y)); + Assert.False(new CieXyChromaticityCoordinates(1, 0) == default); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyyTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyyTests.cs new file mode 100644 index 000000000..245512f8a --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyyTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CieXyyTests +{ + [Fact] + public void CieXyyConstructorAssignsFields() + { + const float x = 75F; + const float y = 64F; + const float yl = 287F; + CieXyy cieXyy = new(x, y, yl); + + Assert.Equal(x, cieXyy.X); + Assert.Equal(y, cieXyy.Y); + Assert.Equal(y, cieXyy.Y); + } + + [Fact] + public void CieXyyEquality() + { + CieXyy x = default; + CieXyy y = new(Vector3.One); + + Assert.True(default == default(CieXyy)); + Assert.False(default != default(CieXyy)); + Assert.Equal(default, default(CieXyy)); + Assert.Equal(new CieXyy(1, 0, 1), new CieXyy(1, 0, 1)); + Assert.Equal(new CieXyy(Vector3.One), new CieXyy(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CieXyzTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CieXyzTests.cs new file mode 100644 index 000000000..88138304a --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CieXyzTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CieXyzTests +{ + [Fact] + public void CieXyzConstructorAssignsFields() + { + const float x = 75F; + const float y = 64F; + const float z = 287F; + CieXyz cieXyz = new(x, y, z); + + Assert.Equal(x, cieXyz.X); + Assert.Equal(y, cieXyz.Y); + Assert.Equal(z, cieXyz.Z); + } + + [Fact] + public void CieXyzEquality() + { + CieXyz x = default; + CieXyz y = new(Vector3.One); + + Assert.True(default == default(CieXyz)); + Assert.False(default != default(CieXyz)); + Assert.Equal(default, default(CieXyz)); + Assert.Equal(new CieXyz(1, 0, 1), new CieXyz(1, 0, 1)); + Assert.Equal(new CieXyz(Vector3.One), new CieXyz(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/CmykTests.cs b/tests/ImageSharp.Tests/ColorProfiles/CmykTests.cs new file mode 100644 index 000000000..e2044a75d --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/CmykTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class CmykTests +{ + [Fact] + public void CmykConstructorAssignsFields() + { + const float c = .75F; + const float m = .64F; + const float y = .87F; + const float k = .334F; + Cmyk cmyk = new(c, m, y, k); + + Assert.Equal(c, cmyk.C); + Assert.Equal(m, cmyk.M); + Assert.Equal(y, cmyk.Y); + Assert.Equal(k, cmyk.K); + } + + [Fact] + public void CmykEquality() + { + Cmyk x = default; + Cmyk y = new(Vector4.One); + + Assert.True(default == default(Cmyk)); + Assert.False(default != default(Cmyk)); + Assert.Equal(default, default(Cmyk)); + Assert.Equal(new Cmyk(1, 0, 1, 0), new Cmyk(1, 0, 1, 0)); + Assert.Equal(new Cmyk(Vector4.One), new Cmyk(Vector4.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs b/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs new file mode 100644 index 000000000..61eb3db66 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorSpaces; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class HslTests +{ + [Fact] + public void HslConstructorAssignsFields() + { + const float h = 275F; + const float s = .64F; + const float l = .87F; + Hsl hsl = new(h, s, l); + + Assert.Equal(h, hsl.H); + Assert.Equal(s, hsl.S); + Assert.Equal(l, hsl.L); + } + + [Fact] + public void HslEquality() + { + Hsl x = default; + Hsl y = new(Vector3.One); + + Assert.True(default == default(Hsl)); + Assert.False(default != default(Hsl)); + Assert.Equal(default, default(Hsl)); + Assert.Equal(new Hsl(1, 0, 1), new Hsl(1, 0, 1)); + Assert.Equal(new Hsl(Vector3.One), new Hsl(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/HsvTests.cs b/tests/ImageSharp.Tests/ColorProfiles/HsvTests.cs new file mode 100644 index 000000000..46f58b18e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/HsvTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class HsvTests +{ + [Fact] + public void HsvConstructorAssignsFields() + { + const float h = 275F; + const float s = .64F; + const float v = .87F; + Hsv hsv = new(h, s, v); + + Assert.Equal(h, hsv.H); + Assert.Equal(s, hsv.S); + Assert.Equal(v, hsv.V); + } + + [Fact] + public void HsvEquality() + { + Hsv x = default; + Hsv y = new(Vector3.One); + + Assert.True(default == default(Hsv)); + Assert.False(default != default(Hsv)); + Assert.Equal(default, default(Hsv)); + Assert.Equal(new Hsv(1, 0, 1), new Hsv(1, 0, 1)); + Assert.Equal(new Hsv(Vector3.One), new Hsv(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/HunterLabTests.cs b/tests/ImageSharp.Tests/ColorProfiles/HunterLabTests.cs new file mode 100644 index 000000000..5fbdd0788 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/HunterLabTests.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class HunterLabTests +{ + [Fact] + public void HunterLabConstructorAssignsFields() + { + const float l = 75F; + const float a = -64F; + const float b = 87F; + HunterLab hunterLab = new(l, a, b); + + Assert.Equal(l, hunterLab.L); + Assert.Equal(a, hunterLab.A); + Assert.Equal(b, hunterLab.B); + } + + [Fact] + public void HunterLabEquality() + { + HunterLab x = default; + HunterLab y = new(Vector3.One); + + Assert.True(default == default(HunterLab)); + Assert.True(new HunterLab(1, 0, 1) != default); + Assert.False(new HunterLab(1, 0, 1) == default); + Assert.Equal(default, default(HunterLab)); + Assert.Equal(new HunterLab(1, 0, 1), new HunterLab(1, 0, 1)); + Assert.Equal(new HunterLab(Vector3.One), new HunterLab(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/LmsTests.cs b/tests/ImageSharp.Tests/ColorProfiles/LmsTests.cs new file mode 100644 index 000000000..138fd544d --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/LmsTests.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class LmsTests +{ + [Fact] + public void LmsConstructorAssignsFields() + { + const float l = 75F; + const float m = -64F; + const float s = 87F; + Lms lms = new(l, m, s); + + Assert.Equal(l, lms.L); + Assert.Equal(m, lms.M); + Assert.Equal(s, lms.S); + } + + [Fact] + public void LmsEquality() + { + Lms x = default; + Lms y = new(Vector3.One); + + Assert.True(default == default(Lms)); + Assert.True(new Lms(1, 0, 1) != default); + Assert.False(new Lms(1, 0, 1) == default); + Assert.Equal(default, default(Lms)); + Assert.Equal(new Lms(1, 0, 1), new Lms(1, 0, 1)); + Assert.Equal(new Lms(Vector3.One), new Lms(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbTests.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbTests.cs new file mode 100644 index 000000000..7e4d4ee0e --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbTests.cs @@ -0,0 +1,81 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class RgbTests +{ + [Fact] + public void RgbConstructorAssignsFields() + { + const float r = .75F; + const float g = .64F; + const float b = .87F; + Rgb rgb = new(r, g, b); + + Assert.Equal(r, rgb.R); + Assert.Equal(g, rgb.G); + Assert.Equal(b, rgb.B); + } + + [Fact] + public void RgbEquality() + { + Rgb x = default; + Rgb y = new(Vector3.One); + + Assert.True(default == default(Rgb)); + Assert.False(default != default(Rgb)); + Assert.Equal(default, default(Rgb)); + Assert.Equal(new Rgb(1, 0, 1), new Rgb(1, 0, 1)); + Assert.Equal(new Rgb(Vector3.One), new Rgb(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } + + [Fact] + public void RgbAndRgb24Interop() + { + const byte r = 64; + const byte g = 128; + const byte b = 255; + + Rgb24 rgb24 = Rgb24.FromScaledVector4(new Rgb(r / 255F, g / 255F, b / 255F).ToScaledVector4()); + Rgb rgb2 = Rgb.FromScaledVector4(rgb24.ToScaledVector4()); + + Assert.Equal(r, rgb24.R); + Assert.Equal(g, rgb24.G); + Assert.Equal(b, rgb24.B); + + Assert.Equal(r / 255F, rgb2.R); + Assert.Equal(g / 255F, rgb2.G); + Assert.Equal(b / 255F, rgb2.B); + } + + [Fact] + public void RgbAndRgba32Interop() + { + const byte r = 64; + const byte g = 128; + const byte b = 255; + + Rgba32 rgba32 = Rgba32.FromScaledVector4(new Rgb(r / 255F, g / 255F, b / 255F).ToScaledVector4()); + Rgb rgb2 = Rgb.FromScaledVector4(rgba32.ToScaledVector4()); + + Assert.Equal(r, rgba32.R); + Assert.Equal(g, rgba32.G); + Assert.Equal(b, rgba32.B); + + Assert.Equal(r / 255F, rgb2.R); + Assert.Equal(g / 255F, rgb2.G); + Assert.Equal(b / 255F, rgb2.B); + } +} diff --git a/tests/ImageSharp.Tests/ColorProfiles/YCbCrTests.cs b/tests/ImageSharp.Tests/ColorProfiles/YCbCrTests.cs new file mode 100644 index 000000000..f8404ad94 --- /dev/null +++ b/tests/ImageSharp.Tests/ColorProfiles/YCbCrTests.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Numerics; +using SixLabors.ImageSharp.ColorProfiles; + +namespace SixLabors.ImageSharp.Tests.ColorProfiles; + +/// +/// Tests the struct. +/// +public class YCbCrTests +{ + [Fact] + public void YCbCrConstructorAssignsFields() + { + const float y = 75F; + const float cb = 64F; + const float cr = 87F; + YCbCr yCbCr = new(y, cb, cr); + + Assert.Equal(y, yCbCr.Y); + Assert.Equal(cb, yCbCr.Cb); + Assert.Equal(cr, yCbCr.Cr); + } + + [Fact] + public void YCbCrEquality() + { + YCbCr x = default; + YCbCr y = new(Vector3.One); + + Assert.True(default == default(YCbCr)); + Assert.False(default != default(YCbCr)); + Assert.Equal(default, default(YCbCr)); + Assert.Equal(new YCbCr(1, 0, 1), new YCbCr(1, 0, 1)); + Assert.Equal(new YCbCr(Vector3.One), new YCbCr(Vector3.One)); + Assert.False(x.Equals(y)); + Assert.False(x.Equals((object)y)); + Assert.False(x.GetHashCode().Equals(y.GetHashCode())); + } +} From 355bd04b8ddb1734aab7f28c10ef7716979a4b77 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 May 2024 12:02:42 +1000 Subject: [PATCH 40/79] Update benchmarks --- .../ColorProfiles/ColorConversionOptions.cs | 1 - .../KnownChromaticAdaptationMatrices.cs | 1 - .../ColorProfiles/KnownRgbWorkingSpaces.cs | 5 ++-- .../Color/ColorEquality.cs | 14 ++++------- .../Color/ColorspaceCieXyzToCieLabConvert.cs | 23 ++++++------------- .../ColorspaceCieXyzToHunterLabConvert.cs | 23 ++++++------------- .../Color/ColorspaceCieXyzToLmsConvert.cs | 23 ++++++------------- .../Color/ColorspaceCieXyzToRgbConvert.cs | 23 ++++++------------- .../Color/RgbWorkingSpaceAdapt.cs | 23 ++++++------------- .../ImageSharp.Benchmarks/Color/YcbCrToRgb.cs | 12 +++++----- 10 files changed, 47 insertions(+), 101 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs index 93eb8bde1..1eb118834 100644 --- a/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs +++ b/src/ImageSharp/ColorProfiles/ColorConversionOptions.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Numerics; -using SixLabors.ColorProfiles; using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; using SixLabors.ImageSharp.Memory; diff --git a/src/ImageSharp/ColorProfiles/KnownChromaticAdaptationMatrices.cs b/src/ImageSharp/ColorProfiles/KnownChromaticAdaptationMatrices.cs index cbc76332b..71d565f87 100644 --- a/src/ImageSharp/ColorProfiles/KnownChromaticAdaptationMatrices.cs +++ b/src/ImageSharp/ColorProfiles/KnownChromaticAdaptationMatrices.cs @@ -18,7 +18,6 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// These matrices are typically used in conjunction with color space conversions, such as from XYZ /// to RGB, to achieve accurate color rendition in digital imaging applications. /// - public static class KnownChromaticAdaptationMatrices { /// diff --git a/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs b/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs index dfb1ec631..916383936 100644 --- a/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs +++ b/src/ImageSharp/ColorProfiles/KnownRgbWorkingSpaces.cs @@ -1,11 +1,10 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.ColorProfiles; using SixLabors.ImageSharp.ColorProfiles.Companding; using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; -namespace SixLabors.ColorProfiles; +namespace SixLabors.ImageSharp.ColorProfiles; /// /// Chromaticity coordinates based on: diff --git a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs index 1ff8013b2..5166c89a9 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorEquality.cs @@ -1,25 +1,19 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; - using SixLabors.ImageSharp.PixelFormats; +using SystemColor = System.Drawing.Color; namespace SixLabors.ImageSharp.Benchmarks; -using SystemColor = System.Drawing.Color; - public class ColorEquality { [Benchmark(Baseline = true, Description = "System.Drawing Color Equals")] public bool SystemDrawingColorEqual() - { - return SystemColor.FromArgb(128, 128, 128, 128).Equals(SystemColor.FromArgb(128, 128, 128, 128)); - } + => SystemColor.FromArgb(128, 128, 128, 128).Equals(SystemColor.FromArgb(128, 128, 128, 128)); [Benchmark(Description = "ImageSharp Color Equals")] public bool ColorEqual() - { - return new Rgba32(128, 128, 128, 128).Equals(new Rgba32(128, 128, 128, 128)); - } + => new Rgba32(128, 128, 128, 128).Equals(new Rgba32(128, 128, 128, 128)); } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs index da09a8523..1f8a6b193 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToCieLabConvert.cs @@ -2,34 +2,25 @@ // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; - using Colourful; - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorProfiles; using Illuminants = Colourful.Illuminants; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; +namespace SixLabors.ImageSharp.Benchmarks.ColorProfiles; public class ColorspaceCieXyzToCieLabConvert { - private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); + private static readonly CieXyz CieXyz = new(0.95047F, 1, 1.08883F); - private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); + private static readonly XYZColor XYZColor = new(0.95047, 1, 1.08883); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + private static readonly ColorProfileConverter ColorProfileConverter = new(); private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ(Illuminants.D50).ToLab(Illuminants.D50).Build(); [Benchmark(Baseline = true, Description = "Colourful Convert")] - public double ColourfulConvert() - { - return ColourfulConverter.Convert(XYZColor).L; - } + public double ColourfulConvert() => ColourfulConverter.Convert(XYZColor).L; [Benchmark(Description = "ImageSharp Convert")] - public float ColorSpaceConvert() - { - return ColorSpaceConverter.ToCieLab(CieXyz).L; - } + public float ColorSpaceConvert() => ColorProfileConverter.Convert(CieXyz).L; } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs index c3317c5d9..b11e78819 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToHunterLabConvert.cs @@ -2,34 +2,25 @@ // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; - using Colourful; - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorProfiles; using Illuminants = Colourful.Illuminants; -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; +namespace SixLabors.ImageSharp.Benchmarks.ColorProfiles; public class ColorspaceCieXyzToHunterLabConvert { - private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); + private static readonly CieXyz CieXyz = new(0.95047F, 1, 1.08883F); - private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); + private static readonly XYZColor XYZColor = new(0.95047, 1, 1.08883); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + private static readonly ColorProfileConverter ColorProfileConverter = new(); private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ(Illuminants.C).ToHunterLab(Illuminants.C).Build(); [Benchmark(Baseline = true, Description = "Colourful Convert")] - public double ColourfulConvert() - { - return ColourfulConverter.Convert(XYZColor).L; - } + public double ColourfulConvert() => ColourfulConverter.Convert(XYZColor).L; [Benchmark(Description = "ImageSharp Convert")] - public float ColorSpaceConvert() - { - return ColorSpaceConverter.ToHunterLab(CieXyz).L; - } + public float ColorSpaceConvert() => ColorProfileConverter.Convert(CieXyz).L; } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs index ba213e5b0..a2c7966d4 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToLmsConvert.cs @@ -2,33 +2,24 @@ // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; - using Colourful; +using SixLabors.ImageSharp.ColorProfiles; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; +namespace SixLabors.ImageSharp.Benchmarks.ColorProfiles; public class ColorspaceCieXyzToLmsConvert { - private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); + private static readonly CieXyz CieXyz = new(0.95047F, 1, 1.08883F); - private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); + private static readonly XYZColor XYZColor = new(0.95047, 1, 1.08883); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + private static readonly ColorProfileConverter ColorProfileConverter = new(); private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ().ToLMS().Build(); [Benchmark(Baseline = true, Description = "Colourful Convert")] - public double ColourfulConvert() - { - return ColourfulConverter.Convert(XYZColor).L; - } + public double ColourfulConvert() => ColourfulConverter.Convert(XYZColor).L; [Benchmark(Description = "ImageSharp Convert")] - public float ColorSpaceConvert() - { - return ColorSpaceConverter.ToLms(CieXyz).L; - } + public float ColorSpaceConvert() => ColorProfileConverter.Convert(CieXyz).L; } diff --git a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs index d7a5deafa..b63f92504 100644 --- a/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs +++ b/tests/ImageSharp.Benchmarks/Color/ColorspaceCieXyzToRgbConvert.cs @@ -2,33 +2,24 @@ // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; - using Colourful; +using SixLabors.ImageSharp.ColorProfiles; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; +namespace SixLabors.ImageSharp.Benchmarks.ColorProfiles; public class ColorspaceCieXyzToRgbConvert { - private static readonly CieXyz CieXyz = new CieXyz(0.95047F, 1, 1.08883F); + private static readonly CieXyz CieXyz = new(0.95047F, 1, 1.08883F); - private static readonly XYZColor XYZColor = new XYZColor(0.95047, 1, 1.08883); + private static readonly XYZColor XYZColor = new(0.95047, 1, 1.08883); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(); + private static readonly ColorProfileConverter ColorProfileConverter = new(); private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromXYZ(RGBWorkingSpaces.sRGB.WhitePoint).ToRGB(RGBWorkingSpaces.sRGB).Build(); [Benchmark(Baseline = true, Description = "Colourful Convert")] - public double ColourfulConvert() - { - return ColourfulConverter.Convert(XYZColor).R; - } + public double ColourfulConvert() => ColourfulConverter.Convert(XYZColor).R; [Benchmark(Description = "ImageSharp Convert")] - public float ColorSpaceConvert() - { - return ColorSpaceConverter.ToRgb(CieXyz).R; - } + public float ColorSpaceConvert() => ColorProfileConverter.Convert(CieXyz).R; } diff --git a/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs b/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs index 48b4880d9..6cd8df3fc 100644 --- a/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs +++ b/tests/ImageSharp.Benchmarks/Color/RgbWorkingSpaceAdapt.cs @@ -2,33 +2,24 @@ // Licensed under the Six Labors Split License. using BenchmarkDotNet.Attributes; - using Colourful; +using SixLabors.ImageSharp.ColorProfiles; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Benchmarks.ColorSpaces; +namespace SixLabors.ImageSharp.Benchmarks.ColorProfiles; public class RgbWorkingSpaceAdapt { - private static readonly Rgb Rgb = new Rgb(0.206162F, 0.260277F, 0.746717F, RgbWorkingSpaces.WideGamutRgb); + private static readonly Rgb Rgb = new(0.206162F, 0.260277F, 0.746717F); - private static readonly RGBColor RGBColor = new RGBColor(0.206162, 0.260277, 0.746717); + private static readonly RGBColor RGBColor = new(0.206162, 0.260277, 0.746717); - private static readonly ColorSpaceConverter ColorSpaceConverter = new ColorSpaceConverter(new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }); + private static readonly ColorProfileConverter ColorProfileConverter = new(new ColorConversionOptions { RgbWorkingSpace = KnownRgbWorkingSpaces.WideGamutRgb, TargetRgbWorkingSpace = KnownRgbWorkingSpaces.SRgb }); private static readonly IColorConverter ColourfulConverter = new ConverterBuilder().FromRGB(RGBWorkingSpaces.WideGamutRGB).ToRGB(RGBWorkingSpaces.sRGB).Build(); [Benchmark(Baseline = true, Description = "Colourful Adapt")] - public RGBColor ColourfulConvert() - { - return ColourfulConverter.Convert(RGBColor); - } + public RGBColor ColourfulConvert() => ColourfulConverter.Convert(RGBColor); [Benchmark(Description = "ImageSharp Adapt")] - public Rgb ColorSpaceConvert() - { - return ColorSpaceConverter.Adapt(Rgb); - } + public Rgb ColorSpaceConvert() => ColorProfileConverter.Convert(Rgb); } diff --git a/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs b/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs index 14d848bcb..093397ad5 100644 --- a/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs +++ b/tests/ImageSharp.Benchmarks/Color/YcbCrToRgb.cs @@ -11,9 +11,9 @@ public class YcbCrToRgb [Benchmark(Baseline = true, Description = "Floating Point Conversion")] public Vector3 YcbCrToRgba() { - int y = 255; - int cb = 128; - int cr = 128; + const int y = 255; + const int cb = 128; + const int cr = 128; int ccb = cb - 128; int ccr = cr - 128; @@ -28,9 +28,9 @@ public class YcbCrToRgb [Benchmark(Description = "Scaled Integer Conversion")] public Vector3 YcbCrToRgbaScaled() { - int y = 255; - int cb = 128; - int cr = 128; + const int y = 255; + const int cb = 128; + const int cr = 128; int ccb = cb - 128; int ccr = cr - 128; From fb50f5be71605e3b741f0d201ba37ca9d6a0ac8c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 May 2024 12:41:54 +1000 Subject: [PATCH 41/79] Replace old converter --- ...rProfileConverterExtensionsCieLabCieLab.cs | 4 +- ...rProfileConverterExtensionsCieLabCieXyz.cs | 4 +- ...olorProfileConverterExtensionsCieLabRgb.cs | 4 +- ...rProfileConverterExtensionsCieXyzCieLab.cs | 4 +- ...rProfileConverterExtensionsCieXyzCieXyz.cs | 4 +- ...olorProfileConverterExtensionsCieXyzRgb.cs | 4 +- ...olorProfileConverterExtensionsRgbCieLab.cs | 4 +- ...olorProfileConverterExtensionsRgbCieXyz.cs | 4 +- .../ColorProfileConverterExtensionsRgbRgb.cs | 4 +- .../CieLabPlanarTiffColor{TPixel}.cs | 7 +-- .../CieLabTiffColor{TPixel}.cs | 7 +-- .../CmykTiffColor{TPixel}.cs | 6 +- .../PixelFormats/PixelConversionModifiers.cs | 2 +- .../PixelImplementations/Rgb24.cs | 7 ++- .../PixelImplementations/Rgba32.cs | 7 ++- .../PixelFormats/Utils/Vector4Converters.cs | 6 +- ...ProfileConverterChomaticAdaptationTests.cs | 1 - .../ColorProfiles/HslTests.cs | 2 +- .../RgbAndCieXyzConversionTest.cs | 1 - .../Formats/Jpg/JpegColorConverterTests.cs | 59 ++++++++++--------- .../PixelOperations/PixelOperationsTests.cs | 36 +++++------ 21 files changed, 92 insertions(+), 85 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs index 9bfccb62b..41ae4b08f 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieLab.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsCieLabCieLab { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -26,7 +26,7 @@ internal static class ColorProfileConverterExtensionsCieLabCieLab CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs index 4c86c8793..04937e927 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabCieXyz.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsCieLabCieXyz { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -25,7 +25,7 @@ internal static class ColorProfileConverterExtensionsCieLabCieXyz pcsTo = VonKriesChromaticAdaptation.Transform(in pcsTo, whitePoints, options.AdaptationMatrix); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs index 89e03f3ae..47e4d2a80 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieLabRgb.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsCieLabRgb { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -26,7 +26,7 @@ internal static class ColorProfileConverterExtensionsCieLabRgb Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs index de44cb17e..6b1575d04 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieLab.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsCieXyzCieLab { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -25,7 +25,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieLab CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFrom); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs index cea7ea73a..8f56a5a66 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzCieXyz.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsCieXyzCieXyz { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -22,7 +22,7 @@ internal static class ColorProfileConverterExtensionsCieXyzCieXyz pcsFrom = VonKriesChromaticAdaptation.Transform(in pcsFrom, whitePoints, options.AdaptationMatrix); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsFrom); + return TTo.FromProfileConnectingSpace(options, in pcsFrom); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs index 0d267e56f..9cc0bd943 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsCieXyzRgb.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsCieXyzRgb { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -25,7 +25,7 @@ internal static class ColorProfileConverterExtensionsCieXyzRgb Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFrom); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs index 50503bb07..415dd94c3 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieLab.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsRgbCieLab { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -26,7 +26,7 @@ internal static class ColorProfileConverterExtensionsRgbCieLab CieLab pcsTo = CieLab.FromProfileConnectingSpace(options, in pcsFromB); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs index 19d04e953..a13f64577 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbCieXyz.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsRgbCieXyz { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -25,7 +25,7 @@ internal static class ColorProfileConverterExtensionsRgbCieXyz pcsTo = VonKriesChromaticAdaptation.Transform(in pcsTo, whitePoints, options.AdaptationMatrix); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs index f7edd4ba4..c1c75dea1 100644 --- a/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs +++ b/src/ImageSharp/ColorProfiles/ColorProfileConverterExtensionsRgbRgb.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; internal static class ColorProfileConverterExtensionsRgbRgb { - public static TTo Convert(this ColorProfileConverter converter, TFrom source) + public static TTo Convert(this ColorProfileConverter converter, in TFrom source) where TFrom : struct, IColorProfile where TTo : struct, IColorProfile { @@ -26,7 +26,7 @@ internal static class ColorProfileConverterExtensionsRgbRgb Rgb pcsTo = Rgb.FromProfileConnectingSpace(options, in pcsFromB); // Convert to output from PCS - return TTo.FromProfileConnectingSpace(options, pcsTo); + return TTo.FromProfileConnectingSpace(options, in pcsTo); } public static void Convert(this ColorProfileConverter converter, ReadOnlySpan source, Span destination) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs index 6be584581..d6fc7c487 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs @@ -2,8 +2,7 @@ // Licensed under the Six Labors Split License. using System.Buffers; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorProfiles; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -16,7 +15,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; internal class CieLabPlanarTiffColor : TiffBasePlanarColorDecoder where TPixel : unmanaged, IPixel { - private static readonly ColorSpaceConverter ColorSpaceConverter = new(); + private static readonly ColorProfileConverter ColorProfileConverter = new(); private const float Inv255 = 1.0f / 255.0f; @@ -34,7 +33,7 @@ internal class CieLabPlanarTiffColor : TiffBasePlanarColorDecoder(in lab); pixelRow[x] = TPixel.FromScaledVector4(new(rgb.R, rgb.G, rgb.B, 1.0f)); offset++; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs index e5dc574f7..b0236022b 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs @@ -1,8 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorProfiles; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -15,7 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; internal class CieLabTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { - private static readonly ColorSpaceConverter ColorSpaceConverter = new(); + private static readonly ColorProfileConverter ColorProfileConverter = new(); private const float Inv255 = 1f / 255f; /// @@ -30,7 +29,7 @@ internal class CieLabTiffColor : TiffBaseColorDecoder { float l = (data[offset] & 0xFF) * 100f * Inv255; CieLab lab = new(l, (sbyte)data[offset + 1], (sbyte)data[offset + 2]); - Rgb rgb = ColorSpaceConverter.ToRgb(lab); + Rgb rgb = ColorProfileConverter.Convert(in lab); pixelRow[x] = TPixel.FromScaledVector4(new(rgb.R, rgb.G, rgb.B, 1f)); offset += 3; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs index 77baa5351..c7fe2ed07 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CmykTiffColor{TPixel}.cs @@ -1,8 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorProfiles; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -11,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation; internal class CmykTiffColor : TiffBaseColorDecoder where TPixel : unmanaged, IPixel { + private static readonly ColorProfileConverter ColorProfileConverter = new(); private const float Inv255 = 1f / 255f; /// @@ -23,7 +23,7 @@ internal class CmykTiffColor : TiffBaseColorDecoder for (int x = 0; x < pixelRow.Length; x++) { Cmyk cmyk = new(data[offset] * Inv255, data[offset + 1] * Inv255, data[offset + 2] * Inv255, data[offset + 3] * Inv255); - Rgb rgb = ColorSpaceConverter.ToRgb(in cmyk); + Rgb rgb = ColorProfileConverter.Convert(in cmyk); pixelRow[x] = TPixel.FromScaledVector4(new(rgb.R, rgb.G, rgb.B, 1.0f)); offset += 4; diff --git a/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs index 25fc74e08..edc04fa7c 100644 --- a/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs +++ b/src/ImageSharp/PixelFormats/PixelConversionModifiers.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.ColorSpaces.Companding; +using SixLabors.ImageSharp.ColorProfiles.Companding; namespace SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs index ac855d47d..0aa7bad23 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgb24.cs @@ -5,6 +5,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.ColorProfiles; namespace SixLabors.ImageSharp.PixelFormats; @@ -54,13 +55,13 @@ public partial struct Rgb24 : IPixel } /// - /// Allows the implicit conversion of an instance of to a + /// Allows the implicit conversion of an instance of to a /// . /// - /// The instance of to convert. + /// The instance of to convert. /// An instance of . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Rgb24(ColorSpaces.Rgb color) => FromScaledVector4(new Vector4(color.ToVector3(), 1f)); + public static implicit operator Rgb24(Rgb color) => FromScaledVector4(new Vector4(color.ToVector3(), 1f)); /// /// Compares two objects for equality. diff --git a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs index fc347c166..049155343 100644 --- a/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs +++ b/src/ImageSharp/PixelFormats/PixelImplementations/Rgba32.cs @@ -7,6 +7,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using SixLabors.ImageSharp.ColorProfiles; namespace SixLabors.ImageSharp.PixelFormats; @@ -180,13 +181,13 @@ public partial struct Rgba32 : IPixel, IPackedVector } /// - /// Allows the implicit conversion of an instance of to a + /// Allows the implicit conversion of an instance of to a /// . /// - /// The instance of to convert. + /// The instance of to convert. /// An instance of . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Rgba32(ColorSpaces.Rgb color) => FromScaledVector4(new Vector4(color.ToVector3(), 1F)); + public static implicit operator Rgba32(Rgb color) => FromScaledVector4(new Vector4(color.ToVector3(), 1F)); /// /// Compares two objects for equality. diff --git a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs index 8f682ae8f..0a0b5660d 100644 --- a/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs +++ b/src/ImageSharp/PixelFormats/Utils/Vector4Converters.cs @@ -3,7 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; +using SixLabors.ImageSharp.ColorProfiles.Companding; namespace SixLabors.ImageSharp.PixelFormats.Utils; @@ -12,6 +12,8 @@ internal static partial class Vector4Converters /// /// Apply modifiers used requested by ToVector4() conversion. /// + /// The span of vectors. + /// The modifier rule. [MethodImpl(InliningOptions.ShortMethod)] internal static void ApplyForwardConversionModifiers(Span vectors, PixelConversionModifiers modifiers) { @@ -29,6 +31,8 @@ internal static partial class Vector4Converters /// /// Apply modifiers used requested by FromVector4() conversion. /// + /// The span of vectors. + /// The modifier rule. [MethodImpl(InliningOptions.ShortMethod)] internal static void ApplyBackwardConversionModifiers(Span vectors, PixelConversionModifiers modifiers) { diff --git a/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs b/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs index 947f153c2..a90e5b9e8 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/ColorProfileConverterChomaticAdaptationTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ColorProfiles; using SixLabors.ImageSharp.ColorProfiles; namespace SixLabors.ImageSharp.Tests.ColorProfiles; diff --git a/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs b/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs index 61eb3db66..d18e65117 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/HslTests.cs @@ -2,7 +2,7 @@ // Licensed under the Six Labors Split License. using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; +using SixLabors.ImageSharp.ColorProfiles; namespace SixLabors.ImageSharp.Tests.ColorProfiles; diff --git a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs index 960dc50fa..c10aa2c3c 100644 --- a/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs +++ b/tests/ImageSharp.Tests/ColorProfiles/RgbAndCieXyzConversionTest.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ColorProfiles; using SixLabors.ImageSharp.ColorProfiles; namespace SixLabors.ImageSharp.Tests.ColorProfiles; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs index ef9f48a89..7aabdaa58 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegColorConverterTests.cs @@ -3,11 +3,10 @@ using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.X86; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; +using SixLabors.ImageSharp.ColorProfiles; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.Tests.Colorspaces.Conversion; +using SixLabors.ImageSharp.Tests.ColorProfiles; using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit.Abstractions; @@ -24,9 +23,9 @@ public class JpegColorConverterTests private const HwIntrinsics IntrinsicsConfig = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2; - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new(epsilon: Precision); + private static readonly ApproximateColorProfileComparer ColorSpaceComparer = new(epsilon: Precision); - private static readonly ColorSpaceConverter ColorSpaceConverter = new(); + private static readonly ColorProfileConverter ColorSpaceConverter = new(); public static readonly TheoryData Seeds = new() { 1, 2, 3 }; @@ -38,7 +37,7 @@ public class JpegColorConverterTests [Fact] public void GetConverterThrowsExceptionOnInvalidColorSpace() { - JpegColorSpace invalidColorSpace = (JpegColorSpace)(-1); + const JpegColorSpace invalidColorSpace = (JpegColorSpace)(-1); Assert.Throws(() => JpegColorConverterBase.GetConverter(invalidColorSpace, 8)); } @@ -46,7 +45,7 @@ public class JpegColorConverterTests public void GetConverterThrowsExceptionOnInvalidPrecision() { // Valid precisions: 8 & 12 bit - int invalidPrecision = 9; + const int invalidPrecision = 9; Assert.Throws(() => JpegColorConverterBase.GetConverter(JpegColorSpace.YCbCr, invalidPrecision)); } @@ -428,7 +427,8 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] public void FromYCbCrArm(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.YCbCrArm(8), + this.TestConversionToRgb( + new JpegColorConverterBase.YCbCrArm(8), 3, seed, new JpegColorConverterBase.YCbCrScalar(8)); @@ -436,7 +436,8 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] public void FromRgbToYCbCrArm(int seed) => - this.TestConversionFromRgb(new JpegColorConverterBase.YCbCrArm(8), + this.TestConversionFromRgb( + new JpegColorConverterBase.YCbCrArm(8), 3, seed, new JpegColorConverterBase.YCbCrScalar(8), @@ -502,7 +503,8 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] public void FromGrayscaleArm(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.GrayscaleArm(8), + this.TestConversionToRgb( + new JpegColorConverterBase.GrayscaleArm(8), 1, seed, new JpegColorConverterBase.GrayscaleScalar(8)); @@ -510,7 +512,8 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] public void FromRgbToGrayscaleArm(int seed) => - this.TestConversionFromRgb(new JpegColorConverterBase.GrayscaleArm(8), + this.TestConversionFromRgb( + new JpegColorConverterBase.GrayscaleArm(8), 1, seed, new JpegColorConverterBase.GrayscaleScalar(8), @@ -556,7 +559,8 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] public void FromYccKArm64(int seed) => - this.TestConversionToRgb(new JpegColorConverterBase.YccKArm64(8), + this.TestConversionToRgb( + new JpegColorConverterBase.YccKArm64(8), 4, seed, new JpegColorConverterBase.YccKScalar(8)); @@ -564,7 +568,8 @@ public class JpegColorConverterTests [Theory] [MemberData(nameof(Seeds))] public void FromRgbToYccKArm64(int seed) => - this.TestConversionFromRgb(new JpegColorConverterBase.YccKArm64(8), + this.TestConversionFromRgb( + new JpegColorConverterBase.YccKArm64(8), 4, seed, new JpegColorConverterBase.YccKScalar(8), @@ -617,9 +622,9 @@ public class JpegColorConverterTests int componentCount, int seed) { - var rnd = new Random(seed); + Random rnd = new(seed); - var buffers = new Buffer2D[componentCount]; + Buffer2D[] buffers = new Buffer2D[componentCount]; for (int i = 0; i < componentCount; i++) { float[] values = new float[length]; @@ -630,8 +635,8 @@ public class JpegColorConverterTests } // no need to dispose when buffer is not array owner - var memory = new Memory(values); - var source = MemoryGroup.Wrap(memory); + Memory memory = new(values); + MemoryGroup source = MemoryGroup.Wrap(memory); buffers[i] = new Buffer2D(source, values.Length, 1); } @@ -786,9 +791,9 @@ public class JpegColorConverterTests float y = values.Component0[i]; float cb = values.Component1[i]; float cr = values.Component2[i]; - Rgb expected = ColorSpaceConverter.ToRgb(new YCbCr(y, cb, cr)); + Rgb expected = ColorSpaceConverter.Convert(new YCbCr(y, cb, cr)); - Rgb actual = new(result.Component0[i], result.Component1[i], result.Component2[i]); + Rgb actual = Rgb.Clamp(new(result.Component0[i], result.Component1[i], result.Component2[i])); bool equal = ColorSpaceComparer.Equals(expected, actual); Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}"); @@ -810,9 +815,9 @@ public class JpegColorConverterTests r /= MaxColorChannelValue; g /= MaxColorChannelValue; b /= MaxColorChannelValue; - var expected = new Rgb(r, g, b); + Rgb expected = Rgb.Clamp(new(r, g, b)); - var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]); + Rgb actual = Rgb.Clamp(new(result.Component0[i], result.Component1[i], result.Component2[i])); bool equal = ColorSpaceComparer.Equals(expected, actual); Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}"); @@ -823,9 +828,9 @@ public class JpegColorConverterTests float r = values.Component0[i] / MaxColorChannelValue; float g = values.Component1[i] / MaxColorChannelValue; float b = values.Component2[i] / MaxColorChannelValue; - var expected = new Rgb(r, g, b); + Rgb expected = Rgb.Clamp(new(r, g, b)); - var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]); + Rgb actual = Rgb.Clamp(new(result.Component0[i], result.Component1[i], result.Component2[i])); bool equal = ColorSpaceComparer.Equals(expected, actual); Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}"); @@ -834,9 +839,9 @@ public class JpegColorConverterTests private static void ValidateGrayScale(in JpegColorConverterBase.ComponentValues values, in JpegColorConverterBase.ComponentValues result, int i) { float y = values.Component0[i] / MaxColorChannelValue; - var expected = new Rgb(y, y, y); + Rgb expected = Rgb.Clamp(new(y, y, y)); - var actual = new Rgb(result.Component0[i], result.Component0[i], result.Component0[i]); + Rgb actual = Rgb.Clamp(new(result.Component0[i], result.Component0[i], result.Component0[i])); bool equal = ColorSpaceComparer.Equals(expected, actual); Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}"); @@ -852,9 +857,9 @@ public class JpegColorConverterTests float r = c * k / MaxColorChannelValue; float g = m * k / MaxColorChannelValue; float b = y * k / MaxColorChannelValue; - var expected = new Rgb(r, g, b); + Rgb expected = Rgb.Clamp(new(r, g, b)); - var actual = new Rgb(result.Component0[i], result.Component1[i], result.Component2[i]); + Rgb actual = Rgb.Clamp(new(result.Component0[i], result.Component1[i], result.Component2[i])); bool equal = ColorSpaceComparer.Equals(expected, actual); Assert.True(equal, $"Colors {expected} and {actual} are not equal at index {i}"); diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs index 68c282d8a..32b62fc03 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelOperations/PixelOperationsTests.cs @@ -5,7 +5,7 @@ using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; +using SixLabors.ImageSharp.ColorProfiles.Companding; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Common; @@ -161,12 +161,12 @@ public abstract class PixelOperationsTests : MeasureFixture [MemberData(nameof(ArraySizesData))] public void FromCompandedScaledVector4(int count) { - void SourceAction(ref Vector4 v) => SRgbCompanding.Expand(ref v); + void SourceAction(ref Vector4 v) => v = SRgbCompanding.Expand(v); - void ExpectedAction(ref Vector4 v) => SRgbCompanding.Compress(ref v); + void ExpectedAction(ref Vector4 v) => v = SRgbCompanding.Compress(v); - Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => SourceAction(ref v)); - TPixel[] expected = CreateScaledExpectedPixelData(source, (ref Vector4 v) => ExpectedAction(ref v)); + Vector4[] source = CreateVector4TestData(count, SourceAction); + TPixel[] expected = CreateScaledExpectedPixelData(source, ExpectedAction); TestOperation( source, @@ -261,7 +261,7 @@ public abstract class PixelOperationsTests : MeasureFixture { void SourceAction(ref Vector4 v) { - SRgbCompanding.Expand(ref v); + v = SRgbCompanding.Expand(v); if (this.HasUnassociatedAlpha) { @@ -276,11 +276,11 @@ public abstract class PixelOperationsTests : MeasureFixture Numerics.UnPremultiply(ref v); } - SRgbCompanding.Compress(ref v); + v = SRgbCompanding.Compress(v); } - Vector4[] source = CreateVector4TestData(count, (ref Vector4 v) => SourceAction(ref v)); - TPixel[] expected = CreateScaledExpectedPixelData(source, (ref Vector4 v) => ExpectedAction(ref v)); + Vector4[] source = CreateVector4TestData(count, SourceAction); + TPixel[] expected = CreateScaledExpectedPixelData(source, ExpectedAction); TestOperation( source, @@ -385,10 +385,10 @@ public abstract class PixelOperationsTests : MeasureFixture { } - void ExpectedAction(ref Vector4 v) => SRgbCompanding.Expand(ref v); + void ExpectedAction(ref Vector4 v) => v = SRgbCompanding.Expand(v); - TPixel[] source = CreateScaledPixelTestData(count, (ref Vector4 v) => SourceAction(ref v)); - Vector4[] expected = CreateExpectedScaledVector4Data(source, (ref Vector4 v) => ExpectedAction(ref v)); + TPixel[] source = CreateScaledPixelTestData(count, SourceAction); + Vector4[] expected = CreateExpectedScaledVector4Data(source, ExpectedAction); TestOperation( source, @@ -410,8 +410,8 @@ public abstract class PixelOperationsTests : MeasureFixture void ExpectedAction(ref Vector4 v) => Numerics.Premultiply(ref v); - TPixel[] source = CreatePixelTestData(count, (ref Vector4 v) => SourceAction(ref v)); - Vector4[] expected = CreateExpectedVector4Data(source, (ref Vector4 v) => ExpectedAction(ref v)); + TPixel[] source = CreatePixelTestData(count, SourceAction); + Vector4[] expected = CreateExpectedVector4Data(source, ExpectedAction); TestOperation( source, @@ -429,7 +429,7 @@ public abstract class PixelOperationsTests : MeasureFixture void ExpectedAction(ref Vector4 v) => Numerics.Premultiply(ref v); - TPixel[] source = CreateScaledPixelTestData(count, (ref Vector4 v) => SourceAction(ref v)); + TPixel[] source = CreateScaledPixelTestData(count, SourceAction); Vector4[] expected = CreateExpectedScaledVector4Data(source, (ref Vector4 v) => ExpectedAction(ref v)); TestOperation( @@ -452,12 +452,12 @@ public abstract class PixelOperationsTests : MeasureFixture void ExpectedAction(ref Vector4 v) { - SRgbCompanding.Expand(ref v); + v = SRgbCompanding.Expand(v); Numerics.Premultiply(ref v); } - TPixel[] source = CreateScaledPixelTestData(count, (ref Vector4 v) => SourceAction(ref v)); - Vector4[] expected = CreateExpectedScaledVector4Data(source, (ref Vector4 v) => ExpectedAction(ref v)); + TPixel[] source = CreateScaledPixelTestData(count, SourceAction); + Vector4[] expected = CreateExpectedScaledVector4Data(source, ExpectedAction); TestOperation( source, From cb56bae39300ab97febdef43335c4c8d15929515 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 20 May 2024 12:44:11 +1000 Subject: [PATCH 42/79] Remove old implementation --- src/ImageSharp/ColorSpaces/CieLab.cs | 135 ------ src/ImageSharp/ColorSpaces/CieLch.cs | 159 ------ src/ImageSharp/ColorSpaces/CieLchuv.cs | 156 ------ src/ImageSharp/ColorSpaces/CieLuv.cs | 136 ------ src/ImageSharp/ColorSpaces/CieXyy.cs | 99 ---- src/ImageSharp/ColorSpaces/CieXyz.cs | 102 ---- src/ImageSharp/ColorSpaces/Cmyk.cs | 107 ---- .../ColorSpaces/Companding/GammaCompanding.cs | 34 -- .../ColorSpaces/Companding/LCompanding.cs | 36 -- .../Companding/Rec2020Companding.cs | 39 -- .../Companding/Rec709Companding.cs | 35 -- .../ColorSpaces/Companding/SRgbCompanding.cs | 230 --------- .../ColorSpaces/Conversion/CieConstants.cs | 21 - .../Conversion/ColorSpaceConverter.Adapt.cs | 159 ------ .../Conversion/ColorSpaceConverter.CieLab.cs | 441 ----------------- .../Conversion/ColorSpaceConverter.CieLch.cs | 441 ----------------- .../ColorSpaceConverter.CieLchuv.cs | 441 ----------------- .../Conversion/ColorSpaceConverter.CieLuv.cs | 435 ----------------- .../Conversion/ColorSpaceConverter.CieXyy.cs | 437 ----------------- .../Conversion/ColorSpaceConverter.CieXyz.cs | 458 ------------------ .../Conversion/ColorSpaceConverter.Cmyk.cs | 437 ----------------- .../Conversion/ColorSpaceConverter.Hsl.cs | 437 ----------------- .../Conversion/ColorSpaceConverter.Hsv.cs | 437 ----------------- .../ColorSpaceConverter.HunterLab.cs | 430 ---------------- .../ColorSpaceConverter.LinearRgb.cs | 429 ---------------- .../Conversion/ColorSpaceConverter.Lms.cs | 425 ---------------- .../Conversion/ColorSpaceConverter.Rgb.cs | 419 ---------------- .../Conversion/ColorSpaceConverter.YCbCr.cs | 404 --------------- .../Conversion/ColorSpaceConverter.cs | 59 --- .../Conversion/ColorSpaceConverterOptions.cs | 53 -- .../CieXyChromaticityCoordinates.cs | 83 ---- .../Converters/CIeLchToCieLabConverter.cs | 31 -- .../Converters/CieLabToCieLchConverter.cs | 39 -- .../Converters/CieLabToCieXyzConverter.cs | 43 -- .../Converters/CieLchuvToCieLuvConverter.cs | 31 -- .../Converters/CieLuvToCieLchuvConverter.cs | 39 -- .../Converters/CieLuvToCieXyzConverter.cs | 73 --- .../Converters/CieXyzAndCieXyyConverter.cs | 52 -- .../CieXyzAndHunterLabConverterBase.cs | 44 -- .../Converters/CieXyzAndLmsConverter.cs | 69 --- .../Converters/CieXyzToCieLabConverter.cs | 57 --- .../Converters/CieXyzToCieLuvConverter.cs | 86 ---- .../Converters/CieXyzToHunterLabConverter.cs | 65 --- .../Converters/CieXyzToLinearRgbConverter.cs | 55 --- .../Converters/CmykAndRgbConverter.cs | 49 -- .../Converters/HslAndRgbConverter.cs | 158 ------ .../Converters/HsvAndRgbConverter.cs | 128 ----- .../Converters/HunterLabToCieXyzConverter.cs | 37 -- .../LinearRgbAndCieXyzConverterBase.cs | 73 --- .../Converters/LinearRgbToCieXyzConverter.cs | 52 -- .../Converters/LinearRgbToRgbConverter.cs | 25 - .../Converters/RgbToLinearRgbConverter.cs | 25 - .../Converters/YCbCrAndRgbConverter.cs | 55 --- .../Implementation/IChromaticAdaptation.cs | 36 -- .../Implementation/LmsAdaptationMatrix.cs | 133 ----- .../RGBPrimariesChromaticityCoordinates.cs | 88 ---- .../VonKriesChromaticAdaptation.cs | 99 ---- .../WorkingSpaces/GammaWorkingSpace.cs | 64 --- .../WorkingSpaces/LWorkingSpace.cs | 31 -- .../WorkingSpaces/Rec2020WorkingSpace.cs | 31 -- .../WorkingSpaces/Rec709WorkingSpace.cs | 31 -- .../WorkingSpaces/RgbWorkingSpace.cs | 79 --- .../WorkingSpaces/SRgbWorkingSpace.cs | 31 -- src/ImageSharp/ColorSpaces/Hsl.cs | 100 ---- src/ImageSharp/ColorSpaces/Hsv.cs | 98 ---- src/ImageSharp/ColorSpaces/HunterLab.cs | 134 ----- src/ImageSharp/ColorSpaces/Illuminants.cs | 71 --- src/ImageSharp/ColorSpaces/LinearRgb.cs | 142 ------ src/ImageSharp/ColorSpaces/Lms.cs | 103 ---- src/ImageSharp/ColorSpaces/Rgb.cs | 163 ------- .../ColorSpaces/RgbWorkingSpaces.cs | 114 ----- src/ImageSharp/ColorSpaces/YCbCr.cs | 99 ---- .../Colorspaces/CieLabTests.cs | 44 -- .../Colorspaces/CieLchTests.cs | 42 -- .../Colorspaces/CieLchuvTests.cs | 42 -- .../Colorspaces/CieLuvTests.cs | 42 -- .../CieXyChromaticityCoordinatesTests.cs | 41 -- .../Colorspaces/CieXyyTests.cs | 42 -- .../Colorspaces/CieXyzTests.cs | 42 -- .../ImageSharp.Tests/Colorspaces/CmykTests.cs | 44 -- .../Colorspaces/Companding/CompandingTests.cs | 113 ----- .../ApproximateColorspaceComparer.cs | 238 --------- .../CieLabAndCieLchConversionTests.cs | 92 ---- .../CieLabAndCieLchuvConversionTests.cs | 80 --- .../CieLabAndCieLuvConversionTests.cs | 80 --- .../CieLabAndCieXyyConversionTests.cs | 76 --- .../CieLabAndCmykConversionTests.cs | 76 --- .../Conversion/CieLabAndHslConversionTests.cs | 76 --- .../Conversion/CieLabAndHsvConversionTests.cs | 76 --- .../CieLabAndHunterLabConversionTests.cs | 76 --- .../CieLabAndLinearRgbConversionTests.cs | 76 --- .../Conversion/CieLabAndLmsConversionTests.cs | 76 --- .../Conversion/CieLabAndRgbConversionTests.cs | 76 --- .../CieLabAndYCbCrConversionTests.cs | 76 --- .../CieLchAndCieLuvConversionTests.cs | 75 --- .../CieLchAndCieXyyConversionTests.cs | 75 --- .../Conversion/CieLchAndHslConversionTests.cs | 75 --- .../Conversion/CieLchAndHsvConversionTests.cs | 75 --- .../CieLchAndHunterLabConversionTests.cs | 75 --- .../CieLchAndLinearRgbConversionTests.cs | 75 --- .../Conversion/CieLchAndLmsConversionTests.cs | 75 --- .../Conversion/CieLchAndRgbConversionTests.cs | 75 --- .../CieLchAndYCbCrConversionTests.cs | 75 --- .../CieLchuvAndCieLchConversionTests.cs | 75 --- .../CieLchuvAndCieLuvConversionTests.cs | 93 ---- .../CieLchuvAndCmykConversionTests.cs | 76 --- .../CieLuvAndCieXyyConversionTests.cs | 76 --- .../Conversion/CieLuvAndHslConversionTests.cs | 76 --- .../Conversion/CieLuvAndHsvConversionTests.cs | 76 --- .../CieLuvAndHunterLabConversionTests.cs | 76 --- .../CieLuvAndLinearRgbConversionTests.cs | 76 --- .../Conversion/CieLuvAndLmsConversionTests.cs | 76 --- .../Conversion/CieLuvAndRgbConversionTests.cs | 76 --- .../CieLuvAndYCbCrConversionTests.cs | 76 --- .../Conversion/CieXyyAndHslConversionTests.cs | 76 --- .../Conversion/CieXyyAndHsvConversionTests.cs | 76 --- .../CieXyyAndHunterLabConversionTests.cs | 76 --- .../CieXyyAndLinearRgbConversionTests.cs | 76 --- .../Conversion/CieXyyAndLmsConversionTests.cs | 76 --- .../Conversion/CieXyyAndRgbConversionTests.cs | 76 --- .../CieXyyAndYCbCrConversionTests.cs | 76 --- .../CieXyzAndCieLabConversionTest.cs | 93 ---- .../CieXyzAndCieLchConversionTests.cs | 75 --- .../CieXyzAndCieLchuvConversionTests.cs | 75 --- .../CieXyzAndCieLuvConversionTest.cs | 92 ---- .../CieXyzAndCieXyyConversionTest.cs | 76 --- .../Conversion/CieXyzAndHslConversionTests.cs | 76 --- .../Conversion/CieXyzAndHsvConversionTests.cs | 76 --- .../CieXyzAndHunterLabConversionTest.cs | 115 ----- .../Conversion/CieXyzAndLmsConversionTest.cs | 88 ---- .../CieXyzAndYCbCrConversionTests.cs | 76 --- .../CmykAndCieLchConversionTests.cs | 75 --- .../CmykAndCieLuvConversionTests.cs | 76 --- .../CmykAndCieXyyConversionTests.cs | 76 --- .../CmykAndCieXyzConversionTests.cs | 76 --- .../Conversion/CmykAndHslConversionTests.cs | 76 --- .../Conversion/CmykAndHsvConversionTests.cs | 76 --- .../CmykAndHunterLabConversionTests.cs | 76 --- .../Conversion/CmykAndYCbCrConversionTests.cs | 76 --- .../Conversion/ColorConverterAdaptTest.cs | 178 ------- .../Conversion/RgbAndCieXyzConversionTest.cs | 170 ------- .../Conversion/RgbAndCmykConversionTest.cs | 84 ---- .../Conversion/RgbAndHslConversionTest.cs | 90 ---- .../Conversion/RgbAndHsvConversionTest.cs | 88 ---- .../Conversion/RgbAndYCbCrConversionTest.cs | 83 ---- .../VonKriesChromaticAdaptationTests.cs | 38 -- .../ImageSharp.Tests/Colorspaces/HslTests.cs | 42 -- .../ImageSharp.Tests/Colorspaces/HsvTests.cs | 42 -- .../Colorspaces/HunterLabTests.cs | 43 -- .../Colorspaces/LinearRgbTests.cs | 42 -- .../ImageSharp.Tests/Colorspaces/LmsTests.cs | 43 -- .../ImageSharp.Tests/Colorspaces/RgbTests.cs | 81 ---- .../Colorspaces/StringRepresentationTests.cs | 63 --- .../Colorspaces/YCbCrTests.cs | 42 -- 154 files changed, 16910 deletions(-) delete mode 100644 src/ImageSharp/ColorSpaces/CieLab.cs delete mode 100644 src/ImageSharp/ColorSpaces/CieLch.cs delete mode 100644 src/ImageSharp/ColorSpaces/CieLchuv.cs delete mode 100644 src/ImageSharp/ColorSpaces/CieLuv.cs delete mode 100644 src/ImageSharp/ColorSpaces/CieXyy.cs delete mode 100644 src/ImageSharp/ColorSpaces/CieXyz.cs delete mode 100644 src/ImageSharp/ColorSpaces/Cmyk.cs delete mode 100644 src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs delete mode 100644 src/ImageSharp/ColorSpaces/Companding/LCompanding.cs delete mode 100644 src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs delete mode 100644 src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs delete mode 100644 src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/IChromaticAdaptation.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs delete mode 100644 src/ImageSharp/ColorSpaces/Hsl.cs delete mode 100644 src/ImageSharp/ColorSpaces/Hsv.cs delete mode 100644 src/ImageSharp/ColorSpaces/HunterLab.cs delete mode 100644 src/ImageSharp/ColorSpaces/Illuminants.cs delete mode 100644 src/ImageSharp/ColorSpaces/LinearRgb.cs delete mode 100644 src/ImageSharp/ColorSpaces/Lms.cs delete mode 100644 src/ImageSharp/ColorSpaces/Rgb.cs delete mode 100644 src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs delete mode 100644 src/ImageSharp/ColorSpaces/YCbCr.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CieLabTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CieLchTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CieLchuvTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CieLuvTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CieXyyTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CieXyzTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/CmykTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLuvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCmykConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHslConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHsvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHunterLabConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLinearRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLmsConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndYCbCrConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieLuvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieXyyConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHslConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHsvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHunterLabConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLinearRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLmsConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndYCbCrConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLchConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLuvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCmykConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndCieXyyConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHslConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHsvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHunterLabConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLinearRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLmsConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndYCbCrConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHslConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHsvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHunterLabConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLinearRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLmsConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndRgbConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndYCbCrConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLabConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchuvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLuvConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieXyyConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHslConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHsvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHunterLabConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndLmsConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndYCbCrConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLchConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLuvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyyConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyzConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHslConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHsvConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHunterLabConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndYCbCrConversionTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCieXyzConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCmykConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHsvConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndYCbCrConversionTest.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/Conversion/VonKriesChromaticAdaptationTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/HslTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/HsvTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/HunterLabTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/LinearRgbTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/LmsTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/RgbTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs delete mode 100644 tests/ImageSharp.Tests/Colorspaces/YCbCrTests.cs diff --git a/src/ImageSharp/ColorSpaces/CieLab.cs b/src/ImageSharp/ColorSpaces/CieLab.cs deleted file mode 100644 index 2346b395c..000000000 --- a/src/ImageSharp/ColorSpaces/CieLab.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents a CIE L*a*b* 1976 color. -/// -/// -public readonly struct CieLab : IEquatable -{ - /// - /// D50 standard illuminant. - /// Used when reference white is not specified explicitly. - /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The a (green - magenta) component. - /// The b (blue - yellow) component. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLab(float l, float a, float b) - : this(l, a, b, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The a (green - magenta) component. - /// The b (blue - yellow) component. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLab(float l, float a, float b, CieXyz whitePoint) - : this(new Vector3(l, a, b), whitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, a, b components. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLab(Vector3 vector) - : this(vector, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, a, b components. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLab(Vector3 vector, CieXyz whitePoint) - : this() - { - // Not clamping as documentation about this space only indicates "usual" ranges - this.L = vector.X; - this.A = vector.Y; - this.B = vector.Z; - this.WhitePoint = whitePoint; - } - - /// - /// Gets the lightness dimension. - /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white). - /// - public readonly float L { get; } - - /// - /// Gets the a color component. - /// A value usually ranging from -100 to 100. Negative is green, positive magenta. - /// - public readonly float A { get; } - - /// - /// Gets the b color component. - /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow - /// - public readonly float B { get; } - - /// - /// Gets the reference white point of this color - /// - public readonly CieXyz WhitePoint { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(CieLab left, CieLab right) => left.Equals(right); - - /// - /// Compares two objects for inequality - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right); - - /// - public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B, this.WhitePoint); - - /// - public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is CieLab other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(CieLab other) => - this.L.Equals(other.L) - && this.A.Equals(other.A) - && this.B.Equals(other.B) - && this.WhitePoint.Equals(other.WhitePoint); -} diff --git a/src/ImageSharp/ColorSpaces/CieLch.cs b/src/ImageSharp/ColorSpaces/CieLch.cs deleted file mode 100644 index 48e8e2c6d..000000000 --- a/src/ImageSharp/ColorSpaces/CieLch.cs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color. -/// -/// -public readonly struct CieLch : IEquatable -{ - /// - /// D50 standard illuminant. - /// Used when reference white is not specified explicitly. - /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.D50; - - private static readonly Vector3 Min = new(0, -200, 0); - private static readonly Vector3 Max = new(100, 200, 360); - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The chroma, relative saturation. - /// The hue in degrees. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLch(float l, float c, float h) - : this(l, c, h, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The chroma, relative saturation. - /// The hue in degrees. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLch(float l, float c, float h, CieXyz whitePoint) - : this(new Vector3(l, c, h), whitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, c, h components. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLch(Vector3 vector) - : this(vector, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, c, h components. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLch(Vector3 vector, CieXyz whitePoint) - { - vector = Vector3.Clamp(vector, Min, Max); - this.L = vector.X; - this.C = vector.Y; - this.H = vector.Z; - this.WhitePoint = whitePoint; - } - - /// - /// Gets the lightness dimension. - /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). - /// - public readonly float L { get; } - - /// - /// Gets the a chroma component. - /// A value ranging from 0 to 200. - /// - public readonly float C { get; } - - /// - /// Gets the h° hue component in degrees. - /// A value ranging from 0 to 360. - /// - public readonly float H { get; } - - /// - /// Gets the reference white point of this color - /// - public readonly CieXyz WhitePoint { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(CieLch left, CieLch right) => left.Equals(right); - - /// - /// Compares two objects for inequality - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(CieLch left, CieLch right) => !left.Equals(right); - - /// - public override int GetHashCode() - => HashCode.Combine(this.L, this.C, this.H, this.WhitePoint); - - /// - public override string ToString() => FormattableString.Invariant($"CieLch({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override bool Equals(object? obj) => obj is CieLch other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(CieLch other) - => this.L.Equals(other.L) - && this.C.Equals(other.C) - && this.H.Equals(other.H) - && this.WhitePoint.Equals(other.WhitePoint); - - /// - /// Computes the saturation of the color (chroma normalized by lightness) - /// - /// - /// A value ranging from 0 to 100. - /// - /// The - [MethodImpl(InliningOptions.ShortMethod)] - public float Saturation() - { - float result = 100 * (this.C / this.L); - - if (float.IsNaN(result)) - { - return 0; - } - - return result; - } -} diff --git a/src/ImageSharp/ColorSpaces/CieLchuv.cs b/src/ImageSharp/ColorSpaces/CieLchuv.cs deleted file mode 100644 index 4d47be5aa..000000000 --- a/src/ImageSharp/ColorSpaces/CieLchuv.cs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color. -/// -/// -public readonly struct CieLchuv : IEquatable -{ - private static readonly Vector3 Min = new(0, -200, 0); - private static readonly Vector3 Max = new(100, 200, 360); - - /// - /// D50 standard illuminant. - /// Used when reference white is not specified explicitly. - /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The chroma, relative saturation. - /// The hue in degrees. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLchuv(float l, float c, float h) - : this(l, c, h, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The chroma, relative saturation. - /// The hue in degrees. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLchuv(float l, float c, float h, CieXyz whitePoint) - : this(new Vector3(l, c, h), whitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, c, h components. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLchuv(Vector3 vector) - : this(vector, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, c, h components. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLchuv(Vector3 vector, CieXyz whitePoint) - : this() - { - vector = Vector3.Clamp(vector, Min, Max); - this.L = vector.X; - this.C = vector.Y; - this.H = vector.Z; - this.WhitePoint = whitePoint; - } - - /// - /// Gets the lightness dimension. - /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). - /// - public readonly float L { get; } - - /// - /// Gets the a chroma component. - /// A value ranging from 0 to 200. - /// - public readonly float C { get; } - - /// - /// Gets the h° hue component in degrees. - /// A value ranging from 0 to 360. - /// - public readonly float H { get; } - - /// - /// Gets the reference white point of this color - /// - public readonly CieXyz WhitePoint { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - public static bool operator ==(CieLchuv left, CieLchuv right) => left.Equals(right); - - /// - /// Compares two objects for inequality - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - public static bool operator !=(CieLchuv left, CieLchuv right) => !left.Equals(right); - - /// - public override int GetHashCode() => HashCode.Combine(this.L, this.C, this.H, this.WhitePoint); - - /// - public override string ToString() => FormattableString.Invariant($"CieLchuv({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is CieLchuv other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(CieLchuv other) - => this.L.Equals(other.L) - && this.C.Equals(other.C) - && this.H.Equals(other.H) - && this.WhitePoint.Equals(other.WhitePoint); - - /// - /// Computes the saturation of the color (chroma normalized by lightness) - /// - /// - /// A value ranging from 0 to 100. - /// - /// The - [MethodImpl(InliningOptions.ShortMethod)] - public float Saturation() - { - float result = 100 * (this.C / this.L); - - if (float.IsNaN(result)) - { - return 0; - } - - return result; - } -} diff --git a/src/ImageSharp/ColorSpaces/CieLuv.cs b/src/ImageSharp/ColorSpaces/CieLuv.cs deleted file mode 100644 index 04bc96cfa..000000000 --- a/src/ImageSharp/ColorSpaces/CieLuv.cs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// The CIE 1976 (L*, u*, v*) color space, commonly known by its abbreviation CIELUV, is a color space adopted by the International -/// Commission on Illumination (CIE) in 1976, as a simple-to-compute transformation of the 1931 CIE XYZ color space, but which -/// attempted perceptual uniformity -/// -/// -public readonly struct CieLuv : IEquatable -{ - /// - /// D65 standard illuminant. - /// Used when reference white is not specified explicitly. - /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.D65; - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The blue-yellow chromaticity coordinate of the given whitepoint. - /// The red-green chromaticity coordinate of the given whitepoint. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLuv(float l, float u, float v) - : this(l, u, v, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The blue-yellow chromaticity coordinate of the given whitepoint. - /// The red-green chromaticity coordinate of the given whitepoint. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLuv(float l, float u, float v, CieXyz whitePoint) - : this(new Vector3(l, u, v), whitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, u, v components. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLuv(Vector3 vector) - : this(vector, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, u, v components. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public CieLuv(Vector3 vector, CieXyz whitePoint) - { - // Not clamping as documentation about this space only indicates "usual" ranges - this.L = vector.X; - this.U = vector.Y; - this.V = vector.Z; - this.WhitePoint = whitePoint; - } - - /// - /// Gets the lightness dimension - /// A value usually ranging between 0 and 100. - /// - public readonly float L { get; } - - /// - /// Gets the blue-yellow chromaticity coordinate of the given whitepoint. - /// A value usually ranging between -100 and 100. - /// - public readonly float U { get; } - - /// - /// Gets the red-green chromaticity coordinate of the given whitepoint. - /// A value usually ranging between -100 and 100. - /// - public readonly float V { get; } - - /// - /// Gets the reference white point of this color - /// - public readonly CieXyz WhitePoint { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(CieLuv left, CieLuv right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(CieLuv left, CieLuv right) => !left.Equals(right); - - /// - public override int GetHashCode() => HashCode.Combine(this.L, this.U, this.V, this.WhitePoint); - - /// - public override string ToString() => FormattableString.Invariant($"CieLuv({this.L:#0.##}, {this.U:#0.##}, {this.V:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is CieLuv other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(CieLuv other) - => this.L.Equals(other.L) - && this.U.Equals(other.U) - && this.V.Equals(other.V) - && this.WhitePoint.Equals(other.WhitePoint); -} diff --git a/src/ImageSharp/ColorSpaces/CieXyy.cs b/src/ImageSharp/ColorSpaces/CieXyy.cs deleted file mode 100644 index 6b7d2e6cb..000000000 --- a/src/ImageSharp/ColorSpaces/CieXyy.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents an CIE xyY 1931 color -/// -/// -public readonly struct CieXyy : IEquatable -{ - /// - /// Initializes a new instance of the struct. - /// - /// The x chroma component. - /// The y chroma component. - /// The y luminance component. - [MethodImpl(InliningOptions.ShortMethod)] - public CieXyy(float x, float y, float yl) - { - // Not clamping as documentation about this space only indicates "usual" ranges - this.X = x; - this.Y = y; - this.Yl = yl; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the x, y, Y components. - [MethodImpl(InliningOptions.ShortMethod)] - public CieXyy(Vector3 vector) - : this() - { - // Not clamping as documentation about this space only indicates "usual" ranges - this.X = vector.X; - this.Y = vector.Y; - this.Yl = vector.Z; - } - - /// - /// Gets the X chrominance component. - /// A value usually ranging between 0 and 1. - /// - public readonly float X { get; } - - /// - /// Gets the Y chrominance component. - /// A value usually ranging between 0 and 1. - /// - public readonly float Y { get; } - - /// - /// Gets the Y luminance component. - /// A value usually ranging between 0 and 1. - /// - public readonly float Yl { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(CieXyy left, CieXyy right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(CieXyy left, CieXyy right) => !left.Equals(right); - - /// - public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Yl); - - /// - public override string ToString() => FormattableString.Invariant($"CieXyy({this.X:#0.##}, {this.Y:#0.##}, {this.Yl:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is CieXyy other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(CieXyy other) - => this.X.Equals(other.X) - && this.Y.Equals(other.Y) - && this.Yl.Equals(other.Yl); -} diff --git a/src/ImageSharp/ColorSpaces/CieXyz.cs b/src/ImageSharp/ColorSpaces/CieXyz.cs deleted file mode 100644 index 2ac9c9f28..000000000 --- a/src/ImageSharp/ColorSpaces/CieXyz.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents an CIE XYZ 1931 color -/// -/// -public readonly struct CieXyz : IEquatable -{ - /// - /// Initializes a new instance of the struct. - /// - /// X is a mix (a linear combination) of cone response curves chosen to be nonnegative - /// The y luminance component. - /// Z is quasi-equal to blue stimulation, or the S cone of the human eye. - [MethodImpl(InliningOptions.ShortMethod)] - public CieXyz(float x, float y, float z) - : this(new Vector3(x, y, z)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the x, y, z components. - public CieXyz(Vector3 vector) - : this() - { - // Not clamping as documentation about this space only indicates "usual" ranges - this.X = vector.X; - this.Y = vector.Y; - this.Z = vector.Z; - } - - /// - /// Gets the X component. A mix (a linear combination) of cone response curves chosen to be nonnegative. - /// A value usually ranging between 0 and 1. - /// - public readonly float X { get; } - - /// - /// Gets the Y luminance component. - /// A value usually ranging between 0 and 1. - /// - public readonly float Y { get; } - - /// - /// Gets the Z component. Quasi-equal to blue stimulation, or the S cone response. - /// A value usually ranging between 0 and 1. - /// - public readonly float Z { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(CieXyz left, CieXyz right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(CieXyz left, CieXyz right) => !left.Equals(right); - - /// - /// Returns a new representing this instance. - /// - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public Vector3 ToVector3() => new(this.X, this.Y, this.Z); - - /// - public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z); - - /// - public override string ToString() => FormattableString.Invariant($"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is CieXyz other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(CieXyz other) - => this.X.Equals(other.X) - && this.Y.Equals(other.Y) - && this.Z.Equals(other.Z); -} diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs deleted file mode 100644 index a5aacf38a..000000000 --- a/src/ImageSharp/ColorSpaces/Cmyk.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents an CMYK (cyan, magenta, yellow, keyline) color. -/// -public readonly struct Cmyk : IEquatable -{ - private static readonly Vector4 Min = Vector4.Zero; - private static readonly Vector4 Max = Vector4.One; - - /// - /// Initializes a new instance of the struct. - /// - /// The cyan component. - /// The magenta component. - /// The yellow component. - /// The keyline black component. - [MethodImpl(InliningOptions.ShortMethod)] - public Cmyk(float c, float m, float y, float k) - : this(new Vector4(c, m, y, k)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the c, m, y, k components. - [MethodImpl(InliningOptions.ShortMethod)] - public Cmyk(Vector4 vector) - { - vector = Numerics.Clamp(vector, Min, Max); - this.C = vector.X; - this.M = vector.Y; - this.Y = vector.Z; - this.K = vector.W; - } - - /// - /// Gets the cyan color component. - /// A value ranging between 0 and 1. - /// - public readonly float C { get; } - - /// - /// Gets the magenta color component. - /// A value ranging between 0 and 1. - /// - public readonly float M { get; } - - /// - /// Gets the yellow color component. - /// A value ranging between 0 and 1. - /// - public readonly float Y { get; } - - /// - /// Gets the keyline black color component. - /// A value ranging between 0 and 1. - /// - public readonly float K { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(Cmyk left, Cmyk right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(Cmyk left, Cmyk right) => !left.Equals(right); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() => HashCode.Combine(this.C, this.M, this.Y, this.K); - - /// - public override string ToString() => FormattableString.Invariant($"Cmyk({this.C:#0.##}, {this.M:#0.##}, {this.Y:#0.##}, {this.K:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is Cmyk other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(Cmyk other) - => this.C.Equals(other.C) - && this.M.Equals(other.M) - && this.Y.Equals(other.Y) - && this.K.Equals(other.K); -} diff --git a/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs deleted file mode 100644 index e5d98430f..000000000 --- a/src/ImageSharp/ColorSpaces/Companding/GammaCompanding.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Companding; - -/// -/// Implements gamma companding. -/// -/// -/// -/// -/// -public static class GammaCompanding -{ - /// - /// Expands a companded channel to its linear equivalent with respect to the energy. - /// - /// The channel value. - /// The gamma value. - /// The representing the linear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Expand(float channel, float gamma) => MathF.Pow(channel, gamma); - - /// - /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. - /// - /// The channel value. - /// The gamma value. - /// The representing the nonlinear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Compress(float channel, float gamma) => MathF.Pow(channel, 1 / gamma); -} diff --git a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs deleted file mode 100644 index db44fd069..000000000 --- a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.ColorSpaces.Companding; - -/// -/// Implements L* companding. -/// -/// -/// For more info see: -/// -/// -/// -public static class LCompanding -{ - /// - /// Expands a companded channel to its linear equivalent with respect to the energy. - /// - /// The channel value. - /// The representing the linear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Expand(float channel) - => channel <= 0.08F ? (100F * channel) / CieConstants.Kappa : Numerics.Pow3((channel + 0.16F) / 1.16F); - - /// - /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. - /// - /// The channel value - /// The representing the nonlinear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Compress(float channel) - => channel <= CieConstants.Epsilon ? (channel * CieConstants.Kappa) / 100F : (1.16F * MathF.Pow(channel, 0.3333333F)) - 0.16F; -} diff --git a/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs b/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs deleted file mode 100644 index 450097618..000000000 --- a/src/ImageSharp/ColorSpaces/Companding/Rec2020Companding.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Companding; - -/// -/// Implements Rec. 2020 companding function. -/// -/// -/// -/// -public static class Rec2020Companding -{ - private const float Alpha = 1.09929682680944F; - private const float AlphaMinusOne = Alpha - 1F; - private const float Beta = 0.018053968510807F; - private const float InverseBeta = Beta * 4.5F; - private const float Epsilon = 1 / 0.45F; - - /// - /// Expands a companded channel to its linear equivalent with respect to the energy. - /// - /// The channel value. - /// The representing the linear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Expand(float channel) - => channel < InverseBeta ? channel / 4.5F : MathF.Pow((channel + AlphaMinusOne) / Alpha, Epsilon); - - /// - /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. - /// - /// The channel value. - /// The representing the nonlinear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Compress(float channel) - => channel < Beta ? 4.5F * channel : (Alpha * MathF.Pow(channel, 0.45F)) - AlphaMinusOne; -} diff --git a/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs b/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs deleted file mode 100644 index 6e4767bcd..000000000 --- a/src/ImageSharp/ColorSpaces/Companding/Rec709Companding.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Companding; - -/// -/// Implements the Rec. 709 companding function. -/// -/// -/// http://en.wikipedia.org/wiki/Rec._709 -/// -public static class Rec709Companding -{ - private const float Epsilon = 1 / 0.45F; - - /// - /// Expands a companded channel to its linear equivalent with respect to the energy. - /// - /// The channel value. - /// The representing the linear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Expand(float channel) - => channel < 0.081F ? channel / 4.5F : MathF.Pow((channel + 0.099F) / 1.099F, Epsilon); - - /// - /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. - /// - /// The channel value. - /// The representing the nonlinear channel value. - [MethodImpl(InliningOptions.ShortMethod)] - public static float Compress(float channel) - => channel < 0.018F ? 4.5F * channel : (1.099F * MathF.Pow(channel, 0.45F)) - 0.099F; -} diff --git a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs deleted file mode 100644 index 4c3923c88..000000000 --- a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace SixLabors.ImageSharp.ColorSpaces.Companding; - -/// -/// Implements sRGB companding. -/// -/// -/// For more info see: -/// -/// -/// -public static class SRgbCompanding -{ - private const int Length = Scale + 2; // 256kb @ 16bit precision. - private const int Scale = (1 << 16) - 1; - - private static readonly Lazy LazyCompressTable = new( - () => - { - float[] result = new float[Length]; - - for (int i = 0; i < result.Length; i++) - { - double d = (double)i / Scale; - if (d <= (0.04045 / 12.92)) - { - d *= 12.92; - } - else - { - d = (1.055 * Math.Pow(d, 1.0 / 2.4)) - 0.055; - } - - result[i] = (float)d; - } - - return result; - }, - true); - - private static readonly Lazy LazyExpandTable = new( - () => - { - float[] result = new float[Length]; - - for (int i = 0; i < result.Length; i++) - { - double d = (double)i / Scale; - if (d <= 0.04045) - { - d /= 12.92; - } - else - { - d = Math.Pow((d + 0.055) / 1.055, 2.4); - } - - result[i] = (float)d; - } - - return result; - }, - true); - - private static float[] ExpandTable => LazyExpandTable.Value; - - private static float[] CompressTable => LazyCompressTable.Value; - - /// - /// Expands the companded vectors to their linear equivalents with respect to the energy. - /// - /// The span of vectors. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Expand(Span vectors) - { - if (Avx2.IsSupported && vectors.Length >= 2) - { - CompandAvx2(vectors, ExpandTable); - - if (Numerics.Modulo2(vectors.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - Expand(ref MemoryMarshal.GetReference(vectors[^1..])); - } - } - else - { - CompandScalar(vectors, ExpandTable); - } - } - - /// - /// Compresses the uncompanded vectors to their nonlinear equivalents with respect to the energy. - /// - /// The span of vectors. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Compress(Span vectors) - { - if (Avx2.IsSupported && vectors.Length >= 2) - { - CompandAvx2(vectors, CompressTable); - - if (Numerics.Modulo2(vectors.Length) != 0) - { - // Vector4 fits neatly in pairs. Any overlap has to be equal to 1. - Compress(ref MemoryMarshal.GetReference(vectors[^1..])); - } - } - else - { - CompandScalar(vectors, CompressTable); - } - } - - /// - /// Expands a companded vector to its linear equivalent with respect to the energy. - /// - /// The vector. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Expand(ref Vector4 vector) - { - // Alpha is already a linear representation of opacity so we do not want to convert it. - vector.X = Expand(vector.X); - vector.Y = Expand(vector.Y); - vector.Z = Expand(vector.Z); - } - - /// - /// Compresses an uncompanded vector (linear) to its nonlinear equivalent. - /// - /// The vector. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Compress(ref Vector4 vector) - { - // Alpha is already a linear representation of opacity so we do not want to convert it. - vector.X = Compress(vector.X); - vector.Y = Compress(vector.Y); - vector.Z = Compress(vector.Z); - } - - /// - /// Expands a companded channel to its linear equivalent with respect to the energy. - /// - /// The channel value. - /// The representing the linear channel value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Expand(float channel) - => channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F); - - /// - /// Compresses an uncompanded channel (linear) to its nonlinear equivalent. - /// - /// The channel value. - /// The representing the nonlinear channel value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float Compress(float channel) - => channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void CompandAvx2(Span vectors, float[] table) - { - fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table)) - { - var scale = Vector256.Create((float)Scale); - Vector256 zero = Vector256.Zero; - var offset = Vector256.Create(1); - - // Divide by 2 as 4 elements per Vector4 and 8 per Vector256 - ref Vector256 vectorsBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors)); - ref Vector256 vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length / 2u); - - while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) - { - Vector256 multiplied = Avx.Multiply(scale, vectorsBase); - multiplied = Avx.Min(Avx.Max(zero, multiplied), scale); - - Vector256 truncated = Avx.ConvertToVector256Int32WithTruncation(multiplied); - Vector256 truncatedF = Avx.ConvertToVector256Single(truncated); - - Vector256 low = Avx2.GatherVector256(tablePointer, truncated, sizeof(float)); - Vector256 high = Avx2.GatherVector256(tablePointer, Avx2.Add(truncated, offset), sizeof(float)); - - // Alpha is already a linear representation of opacity so we do not want to convert it. - Vector256 companded = Numerics.Lerp(low, high, Avx.Subtract(multiplied, truncatedF)); - vectorsBase = Avx.Blend(companded, vectorsBase, Numerics.BlendAlphaControl); - vectorsBase = ref Unsafe.Add(ref vectorsBase, 1); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void CompandScalar(Span vectors, float[] table) - { - fixed (float* tablePointer = &MemoryMarshal.GetArrayDataReference(table)) - { - Vector4 zero = Vector4.Zero; - var scale = new Vector4(Scale); - ref Vector4 vectorsBase = ref MemoryMarshal.GetReference(vectors); - ref Vector4 vectorsLast = ref Unsafe.Add(ref vectorsBase, (uint)vectors.Length); - - while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) - { - Vector4 multiplied = Numerics.Clamp(vectorsBase * Scale, zero, scale); - - float f0 = multiplied.X; - float f1 = multiplied.Y; - float f2 = multiplied.Z; - - uint i0 = (uint)f0; - uint i1 = (uint)f1; - uint i2 = (uint)f2; - - // Alpha is already a linear representation of opacity so we do not want to convert it. - vectorsBase.X = Numerics.Lerp(tablePointer[i0], tablePointer[i0 + 1], f0 - (int)i0); - vectorsBase.Y = Numerics.Lerp(tablePointer[i1], tablePointer[i1 + 1], f1 - (int)i1); - vectorsBase.Z = Numerics.Lerp(tablePointer[i2], tablePointer[i2 + 1], f2 - (int)i2); - - vectorsBase = ref Unsafe.Add(ref vectorsBase, 1); - } - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs b/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs deleted file mode 100644 index 7c8794404..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/CieConstants.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Constants use for Cie conversion calculations -/// -/// -internal static class CieConstants -{ - /// - /// 216F / 24389F - /// - public const float Epsilon = 0.008856452F; - - /// - /// 24389F / 27F - /// - public const float Kappa = 903.2963F; -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs deleted file mode 100644 index b4934e44a..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Adapt.cs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Performs chromatic adaptation on the various color spaces. -/// -public partial class ColorSpaceConverter -{ - /// - /// Performs chromatic adaptation of given color. - /// Target white point is . - /// - /// The color to adapt - /// The source white point. - /// The adapted color - public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint) => this.Adapt(color, sourceWhitePoint, this.whitePoint); - - /// - /// Performs chromatic adaptation of given color. - /// Target white point is . - /// - /// The color to adapt - /// The source white point. - /// The target white point. - /// The adapted color - public CieXyz Adapt(in CieXyz color, in CieXyz sourceWhitePoint, in CieXyz targetWhitePoint) - { - if (!this.performChromaticAdaptation || sourceWhitePoint.Equals(targetWhitePoint)) - { - return color; - } - - // We know that chromaticAdaption is not null because performChromaticAdaption is checked - return this.chromaticAdaptation!.Transform(color, sourceWhitePoint, targetWhitePoint); - } - - /// - /// Adapts color from the source white point to white point set in . - /// - /// The color to adapt - /// The adapted color - public CieLab Adapt(in CieLab color) - { - if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint)) - { - return color; - } - - var xyzColor = this.ToCieXyz(color); - return this.ToCieLab(xyzColor); - } - - /// - /// Adapts color from the source white point to white point set in . - /// - /// The color to adapt - /// The adapted color - public CieLch Adapt(in CieLch color) - { - if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint)) - { - return color; - } - - var labColor = this.ToCieLab(color); - return this.ToCieLch(labColor); - } - - /// - /// Adapts color from the source white point to white point set in . - /// - /// The color to adapt - /// The adapted color - public CieLchuv Adapt(in CieLchuv color) - { - if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLabWhitePoint)) - { - return color; - } - - var luvColor = this.ToCieLuv(color); - return this.ToCieLchuv(luvColor); - } - - /// - /// Adapts color from the source white point to white point set in . - /// - /// The color to adapt - /// The adapted color - public CieLuv Adapt(in CieLuv color) - { - if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetLuvWhitePoint)) - { - return color; - } - - var xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Adapts color from the source white point to white point set in . - /// - /// The color to adapt - /// The adapted color - public HunterLab Adapt(in HunterLab color) - { - if (!this.performChromaticAdaptation || color.WhitePoint.Equals(this.targetHunterLabWhitePoint)) - { - return color; - } - - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Adapts a color from the source working space to working space set in . - /// - /// The color to adapt - /// The adapted color - public LinearRgb Adapt(in LinearRgb color) - { - if (!this.performChromaticAdaptation || color.WorkingSpace.Equals(this.targetRgbWorkingSpace)) - { - return color; - } - - // Conversion to XYZ - LinearRgbToCieXyzConverter converterToXYZ = GetLinearRgbToCieXyzConverter(color.WorkingSpace); - CieXyz unadapted = converterToXYZ.Convert(color); - - // Adaptation - // We know that chromaticAdaption is not null because performChromaticAdaption is checked - CieXyz adapted = this.chromaticAdaptation!.Transform(unadapted, color.WorkingSpace.WhitePoint, this.targetRgbWorkingSpace.WhitePoint); - - // Conversion back to RGB - return this.cieXyzToLinearRgbConverter.Convert(adapted); - } - - /// - /// Adapts an color from the source working space to working space set in . - /// - /// The color to adapt - /// The adapted color - public Rgb Adapt(in Rgb color) - { - if (!this.performChromaticAdaptation) - { - return color; - } - - var linearInput = ToLinearRgb(color); - LinearRgb linearOutput = this.Adapt(linearInput); - return ToRgb(linearOutput); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs deleted file mode 100644 index 54667ca2a..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLab.cs +++ /dev/null @@ -1,441 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in CieLch color) - { - // Conversion (preserving white point) - CieLab unadapted = CieLchToCieLabConverter.Convert(color); - - return this.Adapt(unadapted); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in CieLchuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in CieXyz color) - { - CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetLabWhitePoint); - - return this.cieXyzToCieLabConverter.Convert(adapted); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in Cmyk color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in Hsl color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in Hsv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in LinearRgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in Rgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieLab ToCieLab(in YCbCr color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLab(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLab(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs deleted file mode 100644 index 9949b5d91..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLch.cs +++ /dev/null @@ -1,441 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in CieLab color) - { - CieLab adapted = this.Adapt(color); - - return CieLabToCieLchConverter.Convert(adapted); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in CieLchuv color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in CieLuv color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in CieXyy color) - { - var xyzColor = ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in CieXyz color) - { - var labColor = this.ToCieLab(color); - - return this.ToCieLch(labColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in Cmyk color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in Hsl color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in Hsv color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in HunterLab color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in LinearRgb color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in Lms color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in Rgb color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLch ToCieLch(in YCbCr color) - { - var xyzColor = this.ToCieXyz(color); - - return this.ToCieLch(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLch destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLch dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLch(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs deleted file mode 100644 index 4b856d118..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLchuv.cs +++ /dev/null @@ -1,441 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in CieLuv color) - { - CieLuv adapted = this.Adapt(color); - - return CieLuvToCieLchuvConverter.Convert(adapted); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in CieXyz color) - { - CieLuv luvColor = this.ToCieLuv(color); - - return this.ToCieLchuv(luvColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in Cmyk color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in Hsl color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in Hsv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in LinearRgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in Rgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLchuv ToCieLchuv(in YCbCr color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLchuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLchuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLchuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLchuv(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs deleted file mode 100644 index 2e8029f64..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieLuv.cs +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in CieLchuv color) - { - // Conversion (preserving white point) - CieLuv unadapted = CieLchuvToCieLuvConverter.Convert(color); - - // Adaptation - return this.Adapt(unadapted); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in CieXyz color) - { - // Adaptation - CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetLuvWhitePoint); - - // Conversion - return this.cieXyzToCieLuvConverter.Convert(adapted); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in Cmyk color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in Hsl color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in Hsv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in LinearRgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in Rgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieLuv ToCieLuv(in YCbCr color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToCieLuv(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref CieLuv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref CieLuv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieLuv(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs deleted file mode 100644 index 13b2a225c..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyy.cs +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in CieLchuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static CieXyy ToCieXyy(in CieXyz color) => CieXyzAndCieXyyConverter.Convert(color); - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in Cmyk color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(Hsl color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in Hsv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in LinearRgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in Rgb color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyy ToCieXyy(in YCbCr color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return ToCieXyy(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyy destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyy dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyy(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs deleted file mode 100644 index 2212ca2e5..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.CieXyz.cs +++ /dev/null @@ -1,458 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Collections.Concurrent; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - private static readonly ConcurrentDictionary ConverterCache = new(); - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in CieLab color) - { - // Conversion - CieXyz unadapted = CieLabToCieXyzConverter.Convert(color); - - // Adaptation - return this.Adapt(unadapted, color.WhitePoint); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in CieLch color) - { - // Conversion to Lab - CieLab labColor = CieLchToCieLabConverter.Convert(color); - - // Conversion to XYZ (incl. adaptation) - return this.ToCieXyz(labColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in CieLchuv color) - { - // Conversion to Luv - CieLuv luvColor = CieLchuvToCieLuvConverter.Convert(color); - - // Conversion to XYZ (incl. adaptation) - return this.ToCieXyz(luvColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in CieLuv color) - { - // Conversion - CieXyz unadapted = CieLuvToCieXyzConverter.Convert(color); - - // Adaptation - return this.Adapt(unadapted, color.WhitePoint); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static CieXyz ToCieXyz(in CieXyy color) - - // Conversion - => CieXyzAndCieXyyConverter.Convert(color); - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in Cmyk color) - { - Rgb rgb = ToRgb(color); - - return this.ToCieXyz(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in Hsl color) - { - Rgb rgb = ToRgb(color); - - return this.ToCieXyz(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in Hsv color) - { - // Conversion - Rgb rgb = ToRgb(color); - - return this.ToCieXyz(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in HunterLab color) - { - CieXyz unadapted = HunterLabToCieXyzConverter.Convert(color); - - return this.Adapt(unadapted, color.WhitePoint); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in LinearRgb color) - { - // Conversion - LinearRgbToCieXyzConverter converter = GetLinearRgbToCieXyzConverter(color.WorkingSpace); - CieXyz unadapted = converter.Convert(color); - - return this.Adapt(unadapted, color.WorkingSpace.WhitePoint); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in Lms color) - => this.cieXyzAndLmsConverter.Convert(color); - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in Rgb color) - { - // Conversion - LinearRgb linear = RgbToLinearRgbConverter.Convert(color); - return this.ToCieXyz(linear); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public CieXyz ToCieXyz(in YCbCr color) - { - Rgb rgb = this.ToRgb(color); - - return this.ToCieXyz(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCieXyz(sp); - } - } - - /// - /// Gets the correct converter for the given rgb working space. - /// - /// The source working space - /// The - private static LinearRgbToCieXyzConverter GetLinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace) - => ConverterCache.GetOrAdd(workingSpace, (key) => new LinearRgbToCieXyzConverter(key)); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs deleted file mode 100644 index ea9a5d734..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Cmyk.cs +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCmyk(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCmyk(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in CieLchuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCmyk(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCmyk(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - - return this.ToCmyk(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in CieXyz color) - { - Rgb rgb = this.ToRgb(color); - - return CmykAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static Cmyk ToCmyk(in Hsl color) - { - Rgb rgb = ToRgb(color); - - return CmykAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static Cmyk ToCmyk(in Hsv color) - { - Rgb rgb = ToRgb(color); - - return CmykAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCmyk(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static Cmyk ToCmyk(in LinearRgb color) - { - Rgb rgb = ToRgb(color); - - return CmykAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToCmyk(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors, - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static Cmyk ToCmyk(in Rgb color) => CmykAndRgbConverter.Convert(color); - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = ToCmyk(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Cmyk ToCmyk(in YCbCr color) - { - Rgb rgb = this.ToRgb(color); - - return CmykAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref Cmyk destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref Cmyk dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToCmyk(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs deleted file mode 100644 index 67ec16291..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsl.cs +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsl(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsl(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(in CieLchuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsl(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsl(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - - return this.ToHsl(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsl ToHsl(in CieXyz color) - { - Rgb rgb = this.ToRgb(color); - - return HslAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Hsl ToHsl(in Cmyk color) - { - Rgb rgb = ToRgb(color); - - return HslAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsl(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Hsl ToHsl(in Hsv color) - { - Rgb rgb = ToRgb(color); - - return HslAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsl(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static Hsl ToHsl(in LinearRgb color) - { - Rgb rgb = ToRgb(color); - - return HslAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsl(xyzColor); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Hsl ToHsl(in Rgb color) => HslAndRgbConverter.Convert(color); - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsl(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public Hsl ToHsl(in YCbCr color) - { - Rgb rgb = this.ToRgb(color); - - return HslAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsl destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsl dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsl(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs deleted file mode 100644 index 47ee42dc5..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Hsv.cs +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in CieLchuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - - return this.ToHsv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in CieXyz color) - { - Rgb rgb = this.ToRgb(color); - - return HsvAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Hsv ToHsv(in Cmyk color) - { - Rgb rgb = ToRgb(color); - - return HsvAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Hsv ToHsv(in Hsl color) - { - Rgb rgb = ToRgb(color); - - return HsvAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors. - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Hsv ToHsv(in LinearRgb color) - { - Rgb rgb = ToRgb(color); - - return HsvAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToHsv(xyzColor); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Hsv ToHsv(in Rgb color) => HsvAndRgbConverter.Convert(color); - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = ToHsv(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Hsv ToHsv(in YCbCr color) - { - Rgb rgb = this.ToRgb(color); - - return HsvAndRgbConverter.Convert(rgb); - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref Hsv destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref Hsv dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHsv(sp); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs deleted file mode 100644 index 060402776..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.HunterLab.cs +++ /dev/null @@ -1,430 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref HunterLab destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref HunterLab dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToHunterLab(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in CieLab color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in CieLch color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in CieLchuv color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in CieLuv color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in CieXyy color) - { - var xyzColor = ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in CieXyz color) - { - CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetHunterLabWhitePoint); - - return this.cieXyzToHunterLabConverter.Convert(adapted); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in Cmyk color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in Hsl color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in Hsv color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in LinearRgb color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in Lms color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in Rgb color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public HunterLab ToHunterLab(in YCbCr color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToHunterLab(xyzColor); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs deleted file mode 100644 index fd385a15b..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.LinearRgb.cs +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToLinearRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref LinearRgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref LinearRgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLinearRgb(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in CieLchuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in CieXyz color) - { - // Adaptation - CieXyz adapted = this.Adapt(color, this.whitePoint, this.targetRgbWorkingSpace.WhitePoint); - - // Conversion - return this.cieXyzToLinearRgbConverter.Convert(adapted); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static LinearRgb ToLinearRgb(in Cmyk color) - { - Rgb rgb = ToRgb(color); - return ToLinearRgb(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static LinearRgb ToLinearRgb(in Hsl color) - { - Rgb rgb = ToRgb(color); - return ToLinearRgb(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static LinearRgb ToLinearRgb(in Hsv color) - { - Rgb rgb = ToRgb(color); - return ToLinearRgb(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToLinearRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static LinearRgb ToLinearRgb(in Rgb color) - => RgbToLinearRgbConverter.Convert(color); - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public LinearRgb ToLinearRgb(in YCbCr color) - { - Rgb rgb = this.ToRgb(color); - return ToLinearRgb(rgb); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs deleted file mode 100644 index 56f61ef80..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Lms.cs +++ /dev/null @@ -1,425 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref Lms destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref Lms dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToLms(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in CieLab color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in CieLch color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in CieLchuv color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in CieLuv color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in CieXyy color) - { - var xyzColor = ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in CieXyz color) => this.cieXyzAndLmsConverter.Convert(color); - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in Cmyk color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in Hsl color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in Hsv color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in HunterLab color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in LinearRgb color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in Rgb color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Lms ToLms(in YCbCr color) - { - var xyzColor = this.ToCieXyz(color); - return this.ToLms(xyzColor); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs deleted file mode 100644 index 080e1fc4b..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.Rgb.cs +++ /dev/null @@ -1,419 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLchuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLchuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref YCbCr sourceRef = ref MemoryMarshal.GetReference(source); - ref Rgb destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref YCbCr sp = ref Unsafe.Add(ref sourceRef, i); - ref Rgb dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToRgb(sp); - } - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in CieLchuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - return this.ToRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in CieXyz color) - { - // Conversion - LinearRgb linear = this.ToLinearRgb(color); - - // Compand - return ToRgb(linear); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Rgb ToRgb(in Cmyk color) => CmykAndRgbConverter.Convert(color); - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Rgb ToRgb(in Hsv color) => HsvAndRgbConverter.Convert(color); - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Rgb ToRgb(in Hsl color) => HslAndRgbConverter.Convert(color); - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public static Rgb ToRgb(in LinearRgb color) => LinearRgbToRgbConverter.Convert(color); - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - return this.ToRgb(xyzColor); - } - - /// - /// Converts a into a - /// - /// The color to convert. - /// The - public Rgb ToRgb(in YCbCr color) - { - // Conversion - Rgb rgb = YCbCrAndRgbConverter.Convert(color); - - // Adaptation - return this.Adapt(rgb); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs deleted file mode 100644 index da8e046ff..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.YCbCr.cs +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Allows conversion to . -/// -public partial class ColorSpaceConverter -{ - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLab sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLab sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors. - /// The span to the destination colors. - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLch sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLch sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieLuv sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieLuv sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyy sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyy sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Cmyk sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Cmyk sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsl sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsl sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Hsv sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Hsv sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref HunterLab sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref HunterLab sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref LinearRgb sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref LinearRgb sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Lms sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Lms sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = this.ToYCbCr(sp); - } - } - - /// - /// Performs the bulk conversion from into . - /// - /// The span to the source colors - /// The span to the destination colors - public static void Convert(ReadOnlySpan source, Span destination) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - ref Rgb sourceRef = ref MemoryMarshal.GetReference(source); - ref YCbCr destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref Rgb sp = ref Unsafe.Add(ref sourceRef, i); - ref YCbCr dp = ref Unsafe.Add(ref destRef, i); - dp = ToYCbCr(sp); - } - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in CieLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToYCbCr(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in CieLch color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToYCbCr(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in CieLuv color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToYCbCr(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in CieXyy color) - { - CieXyz xyzColor = ToCieXyz(color); - - return this.ToYCbCr(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in CieXyz color) - { - Rgb rgb = this.ToRgb(color); - - return YCbCrAndRgbConverter.Convert(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static YCbCr ToYCbCr(in Cmyk color) - { - Rgb rgb = ToRgb(color); - - return YCbCrAndRgbConverter.Convert(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static YCbCr ToYCbCr(in Hsl color) - { - Rgb rgb = ToRgb(color); - - return YCbCrAndRgbConverter.Convert(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static YCbCr ToYCbCr(in Hsv color) - { - Rgb rgb = ToRgb(color); - - return YCbCrAndRgbConverter.Convert(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in HunterLab color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToYCbCr(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static YCbCr ToYCbCr(in LinearRgb color) - { - Rgb rgb = ToRgb(color); - - return YCbCrAndRgbConverter.Convert(rgb); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public YCbCr ToYCbCr(in Lms color) - { - CieXyz xyzColor = this.ToCieXyz(color); - - return this.ToYCbCr(xyzColor); - } - - /// - /// Converts a into a . - /// - /// The color to convert. - /// The - public static YCbCr ToYCbCr(in Rgb color) => YCbCrAndRgbConverter.Convert(color); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs deleted file mode 100644 index b5e3162e6..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverter.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Provides methods to allow the conversion of color values between different color spaces. -/// -public partial class ColorSpaceConverter -{ - // Options. - private static readonly ColorSpaceConverterOptions DefaultOptions = new(); - private readonly Matrix4x4 lmsAdaptationMatrix; - private readonly CieXyz whitePoint; - private readonly CieXyz targetLuvWhitePoint; - private readonly CieXyz targetLabWhitePoint; - private readonly CieXyz targetHunterLabWhitePoint; - private readonly RgbWorkingSpace targetRgbWorkingSpace; - private readonly IChromaticAdaptation? chromaticAdaptation; - private readonly bool performChromaticAdaptation; - private readonly CieXyzAndLmsConverter cieXyzAndLmsConverter; - private readonly CieXyzToCieLabConverter cieXyzToCieLabConverter; - private readonly CieXyzToCieLuvConverter cieXyzToCieLuvConverter; - private readonly CieXyzToHunterLabConverter cieXyzToHunterLabConverter; - private readonly CieXyzToLinearRgbConverter cieXyzToLinearRgbConverter; - - /// - /// Initializes a new instance of the class. - /// - public ColorSpaceConverter() - : this(DefaultOptions) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The configuration options. - public ColorSpaceConverter(ColorSpaceConverterOptions options) - { - Guard.NotNull(options, nameof(options)); - this.whitePoint = options.WhitePoint; - this.targetLuvWhitePoint = options.TargetLuvWhitePoint; - this.targetLabWhitePoint = options.TargetLabWhitePoint; - this.targetHunterLabWhitePoint = options.TargetHunterLabWhitePoint; - this.targetRgbWorkingSpace = options.TargetRgbWorkingSpace; - this.chromaticAdaptation = options.ChromaticAdaptation; - this.performChromaticAdaptation = this.chromaticAdaptation != null; - this.lmsAdaptationMatrix = options.LmsAdaptationMatrix; - - this.cieXyzAndLmsConverter = new CieXyzAndLmsConverter(this.lmsAdaptationMatrix); - this.cieXyzToCieLabConverter = new CieXyzToCieLabConverter(this.targetLabWhitePoint); - this.cieXyzToCieLuvConverter = new CieXyzToCieLuvConverter(this.targetLuvWhitePoint); - this.cieXyzToHunterLabConverter = new CieXyzToHunterLabConverter(this.targetHunterLabWhitePoint); - this.cieXyzToLinearRgbConverter = new CieXyzToLinearRgbConverter(this.targetRgbWorkingSpace); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs b/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs deleted file mode 100644 index 9f576de72..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/ColorSpaceConverterOptions.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Configuration options for the class. -/// -public class ColorSpaceConverterOptions -{ - /// - /// Gets or sets the white point used for chromatic adaptation in conversions from/to XYZ color space. - /// When default, no adaptation will be performed. - /// Defaults to: . - /// - public CieXyz WhitePoint { get; set; } = CieLuv.DefaultWhitePoint; - - /// - /// Gets or sets the white point used *when creating* Luv/LChuv colors. (Luv/LChuv colors on the input already contain the white point information) - /// Defaults to: . - /// - public CieXyz TargetLuvWhitePoint { get; set; } = CieLuv.DefaultWhitePoint; - - /// - /// 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: . - /// - public CieXyz TargetLabWhitePoint { get; set; } = CieLab.DefaultWhitePoint; - - /// - /// Gets or sets the white point used *when creating* HunterLab colors. (HunterLab colors on the input already contain the white point information) - /// Defaults to: . - /// - public CieXyz TargetHunterLabWhitePoint { get; set; } = HunterLab.DefaultWhitePoint; - - /// - /// Gets or sets the target working space used *when creating* RGB colors. (RGB colors on the input already contain the working space information) - /// Defaults to: . - /// - public RgbWorkingSpace TargetRgbWorkingSpace { get; set; } = Rgb.DefaultWorkingSpace; - - /// - /// Gets or sets the chromatic adaptation method used. When null, no adaptation will be performed. - /// - public IChromaticAdaptation? ChromaticAdaptation { get; set; } = new VonKriesChromaticAdaptation(); - - /// - /// Gets or sets transformation matrix used in conversion to and from . - /// - public Matrix4x4 LmsAdaptationMatrix { get; set; } = CieXyzAndLmsConverter.DefaultTransformationMatrix; -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs deleted file mode 100644 index 2cc785d53..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/CieXyChromaticityCoordinates.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -// ReSharper disable CompareOfFloatsByEqualityOperator -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Represents the coordinates of CIEXY chromaticity space. -/// -public readonly struct CieXyChromaticityCoordinates : IEquatable -{ - /// - /// Initializes a new instance of the struct. - /// - /// Chromaticity coordinate x (usually from 0 to 1) - /// Chromaticity coordinate y (usually from 0 to 1) - [MethodImpl(InliningOptions.ShortMethod)] - public CieXyChromaticityCoordinates(float x, float y) - { - this.X = x; - this.Y = y; - } - - /// - /// Gets the chromaticity X-coordinate. - /// - /// - /// Ranges usually from 0 to 1. - /// - public readonly float X { get; } - - /// - /// Gets the chromaticity Y-coordinate - /// - /// - /// Ranges usually from 0 to 1. - /// - public readonly float Y { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) - => left.Equals(right); - - /// - /// Compares two objects for inequality - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(CieXyChromaticityCoordinates left, CieXyChromaticityCoordinates right) - => !left.Equals(right); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() - => HashCode.Combine(this.X, this.Y); - - /// - public override string ToString() - => FormattableString.Invariant($"CieXyChromaticityCoordinates({this.X:#0.##}, {this.Y:#0.##})"); - - /// - public override bool Equals(object? obj) - => obj is CieXyChromaticityCoordinates other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(CieXyChromaticityCoordinates other) - => this.X.Equals(other.X) && this.Y.Equals(other.Y); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs deleted file mode 100644 index f16ce7b0f..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CIeLchToCieLabConverter.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal static class CieLchToCieLabConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieLab Convert(in CieLch input) - { - // Conversion algorithm described here: - // https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC - float l = input.L, c = input.C, hDegrees = input.H; - float hRadians = GeometryUtilities.DegreeToRadian(hDegrees); - - float a = c * MathF.Cos(hRadians); - float b = c * MathF.Sin(hRadians); - - return new CieLab(l, a, b, input.WhitePoint); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs deleted file mode 100644 index 4eeb7695e..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieLchConverter.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal static class CieLabToCieLchConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieLch Convert(in CieLab input) - { - // Conversion algorithm described here: - // https://en.wikipedia.org/wiki/Lab_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC - float l = input.L, a = input.A, b = input.B; - float c = MathF.Sqrt((a * a) + (b * b)); - float hRadians = MathF.Atan2(b, a); - float hDegrees = GeometryUtilities.RadianToDegree(hRadians); - - // Wrap the angle round at 360. - hDegrees %= 360; - - // Make sure it's not negative. - while (hDegrees < 0) - { - hDegrees += 360; - } - - return new CieLch(l, c, hDegrees, input.WhitePoint); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs deleted file mode 100644 index b02ad0000..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal static class CieLabToCieXyzConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieXyz Convert(in CieLab 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 = 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; - - Vector3 wxyz = new(input.WhitePoint.X, input.WhitePoint.Y, input.WhitePoint.Z); - - // Avoids XYZ coordinates out range (restricted by 0 and XYZ reference white) - Vector3 xyzr = Vector3.Clamp(new Vector3(xr, yr, zr), Vector3.Zero, Vector3.One); - - Vector3 xyz = xyzr * wxyz; - return new CieXyz(xyz); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs deleted file mode 100644 index 9a1e79da4..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLchuvToCieLuvConverter.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal static class CieLchuvToCieLuvConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieLuv Convert(in CieLchuv input) - { - // Conversion algorithm described here: - // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 - float l = input.L, c = input.C, hDegrees = input.H; - float hRadians = GeometryUtilities.DegreeToRadian(hDegrees); - - float u = c * MathF.Cos(hRadians); - float v = c * MathF.Sin(hRadians); - - return new CieLuv(l, u, v, input.WhitePoint); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs deleted file mode 100644 index 4756bab82..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieLchuvConverter.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal static class CieLuvToCieLchuvConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieLchuv Convert(in CieLuv input) - { - // Conversion algorithm described here: - // https://en.wikipedia.org/wiki/CIELUV#Cylindrical_representation_.28CIELCH.29 - float l = input.L, a = input.U, b = input.V; - float c = MathF.Sqrt((a * a) + (b * b)); - float hRadians = MathF.Atan2(b, a); - float hDegrees = GeometryUtilities.RadianToDegree(hRadians); - - // Wrap the angle round at 360. - hDegrees %= 360; - - // Make sure it's not negative. - while (hDegrees < 0) - { - hDegrees += 360; - } - - return new CieLchuv(l, c, hDegrees, input.WhitePoint); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs deleted file mode 100644 index 404c4e824..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal static class CieLuvToCieXyzConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - public static CieXyz Convert(in CieLuv input) - { - // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_Luv_to_XYZ.html - float l = input.L, u = input.U, v = input.V; - - float u0 = ComputeU0(input.WhitePoint); - float v0 = ComputeV0(input.WhitePoint); - - float y = l > CieConstants.Kappa * CieConstants.Epsilon - ? Numerics.Pow3((l + 16) / 116) - : l / CieConstants.Kappa; - - float a = ((52 * l / (u + (13 * l * u0))) - 1) / 3; - float b = -5 * y; - const float c = -0.3333333F; - float d = y * ((39 * l / (v + (13 * l * v0))) - 5); - - float x = (d - b) / (a - c); - float z = (x * a) + b; - - if (float.IsNaN(x) || x < 0) - { - x = 0; - } - - if (float.IsNaN(y) || y < 0) - { - y = 0; - } - - if (float.IsNaN(z) || z < 0) - { - z = 0; - } - - return new CieXyz(x, y, z); - } - - /// - /// Calculates the blue-yellow chromacity based on the given whitepoint. - /// - /// The whitepoint - /// The - [MethodImpl(InliningOptions.ShortMethod)] - private static float ComputeU0(in CieXyz input) - => (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z)); - - /// - /// Calculates the red-green chromacity based on the given whitepoint. - /// - /// The whitepoint - /// The - [MethodImpl(InliningOptions.ShortMethod)] - private static float ComputeV0(in CieXyz input) - => (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z)); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs deleted file mode 100644 index 4cc443cf7..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndCieXyyConverter.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between CIE XYZ and CIE xyY. -/// for formulas. -/// -internal static class CieXyzAndCieXyyConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieXyy Convert(in CieXyz input) - { - float x = input.X / (input.X + input.Y + input.Z); - float y = input.Y / (input.X + input.Y + input.Z); - - if (float.IsNaN(x) || float.IsNaN(y)) - { - return new CieXyy(0, 0, input.Y); - } - - return new CieXyy(x, y, input.Y); - } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieXyz Convert(in CieXyy input) - { - if (MathF.Abs(input.Y) < Constants.Epsilon) - { - return new CieXyz(0, 0, input.Yl); - } - - float x = (input.X * input.Yl) / input.Y; - float y = input.Yl; - float z = ((1 - input.X - input.Y) * y) / input.Y; - - return new CieXyz(x, y, z); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs deleted file mode 100644 index ba221108f..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndHunterLabConverterBase.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// The base class for converting between and color spaces. -/// -internal abstract class CieXyzAndHunterLabConverterBase -{ - /// - /// Returns the Ka coefficient that depends upon the whitepoint illuminant. - /// - /// The whitepoint - /// The - [MethodImpl(InliningOptions.ShortMethod)] - public static float ComputeKa(CieXyz whitePoint) - { - if (whitePoint.Equals(Illuminants.C)) - { - return 175F; - } - - return 100F * (175F / 198.04F) * (whitePoint.X + whitePoint.Y); - } - - /// - /// Returns the Kb coefficient that depends upon the whitepoint illuminant. - /// - /// The whitepoint - /// The - [MethodImpl(InliningOptions.ShortMethod)] - public static float ComputeKb(CieXyz whitePoint) - { - if (whitePoint == Illuminants.C) - { - return 70F; - } - - return 100F * (70F / 218.11F) * (whitePoint.Y + whitePoint.Z); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs deleted file mode 100644 index b5f8a70b6..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzAndLmsConverter.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and -/// -internal sealed class CieXyzAndLmsConverter -{ - /// - /// Default transformation matrix used, when no other is set. (Bradford) - /// - /// - public static readonly Matrix4x4 DefaultTransformationMatrix = LmsAdaptationMatrix.Bradford; - - private Matrix4x4 inverseTransformationMatrix; - private Matrix4x4 transformationMatrix; - - /// - /// Initializes a new instance of the class. - /// - public CieXyzAndLmsConverter() - : this(DefaultTransformationMatrix) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// Definition of the cone response domain (see ), - /// if not set will be used. - /// - public CieXyzAndLmsConverter(Matrix4x4 transformationMatrix) - { - this.transformationMatrix = transformationMatrix; - Matrix4x4.Invert(this.transformationMatrix, out this.inverseTransformationMatrix); - } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public Lms Convert(in CieXyz input) - { - Vector3 vector = Vector3.Transform(input.ToVector3(), this.transformationMatrix); - - return new Lms(vector); - } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public CieXyz Convert(in Lms input) - { - Vector3 vector = Vector3.Transform(input.ToVector3(), this.inverseTransformationMatrix); - - return new CieXyz(vector); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs deleted file mode 100644 index 0ce6e3c9f..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLabConverter.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal sealed class CieXyzToCieLabConverter -{ - /// - /// Initializes a new instance of the class. - /// - public CieXyzToCieLabConverter() - : this(CieLab.DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The target reference lab white point - public CieXyzToCieLabConverter(CieXyz labWhitePoint) => this.LabWhitePoint = labWhitePoint; - - /// - /// Gets the target reference whitepoint. When not set, is used. - /// - public CieXyz LabWhitePoint { get; } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public CieLab Convert(in CieXyz 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; - - 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, this.LabWhitePoint); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs deleted file mode 100644 index 1e17ae54f..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToCieLuvConverter.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Converts from to . -/// -internal sealed class CieXyzToCieLuvConverter -{ - /// - /// Initializes a new instance of the class. - /// - public CieXyzToCieLuvConverter() - : this(CieLuv.DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The target reference luv white point - public CieXyzToCieLuvConverter(CieXyz luvWhitePoint) => this.LuvWhitePoint = luvWhitePoint; - - /// - /// Gets the target reference whitepoint. When not set, is used. - /// - public CieXyz LuvWhitePoint { get; } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - public CieLuv Convert(in CieXyz input) - { - // Conversion algorithm described here: http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_Luv.html - float yr = input.Y / this.LuvWhitePoint.Y; - float up = ComputeUp(input); - float vp = ComputeVp(input); - float upr = ComputeUp(this.LuvWhitePoint); - float vpr = ComputeVp(this.LuvWhitePoint); - - float l = yr > CieConstants.Epsilon ? ((116 * MathF.Pow(yr, 0.3333333F)) - 16F) : (CieConstants.Kappa * yr); - - if (float.IsNaN(l) || l < 0) - { - l = 0; - } - - float u = 13 * l * (up - upr); - float v = 13 * l * (vp - vpr); - - if (float.IsNaN(u)) - { - u = 0; - } - - if (float.IsNaN(v)) - { - v = 0; - } - - return new CieLuv(l, u, v, this.LuvWhitePoint); - } - - /// - /// Calculates the blue-yellow chromacity based on the given whitepoint. - /// - /// The whitepoint - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float ComputeUp(in CieXyz input) - => (4 * input.X) / (input.X + (15 * input.Y) + (3 * input.Z)); - - /// - /// Calculates the red-green chromacity based on the given whitepoint. - /// - /// The whitepoint - /// The - [MethodImpl(InliningOptions.ShortMethod)] - private static float ComputeVp(in CieXyz input) - => (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z)); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs deleted file mode 100644 index dab953a74..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToHunterLabConverter.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and -/// -internal sealed class CieXyzToHunterLabConverter : CieXyzAndHunterLabConverterBase -{ - /// - /// Initializes a new instance of the class. - /// - public CieXyzToHunterLabConverter() - : this(HunterLab.DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The hunter Lab white point. - public CieXyzToHunterLabConverter(CieXyz labWhitePoint) => this.HunterLabWhitePoint = labWhitePoint; - - /// - /// Gets the target reference white. When not set, is used. - /// - public CieXyz HunterLabWhitePoint { get; } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public HunterLab Convert(in CieXyz input) - { - // Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab - float x = input.X, y = input.Y, z = input.Z; - float xn = this.HunterLabWhitePoint.X, yn = this.HunterLabWhitePoint.Y, zn = this.HunterLabWhitePoint.Z; - - float ka = ComputeKa(this.HunterLabWhitePoint); - float kb = ComputeKb(this.HunterLabWhitePoint); - - float yByYn = y / yn; - float sqrtYbyYn = MathF.Sqrt(yByYn); - float l = 100 * sqrtYbyYn; - float a = ka * (((x / xn) - yByYn) / sqrtYbyYn); - float b = kb * ((yByYn - (z / zn)) / sqrtYbyYn); - - if (float.IsNaN(a)) - { - a = 0; - } - - if (float.IsNaN(b)) - { - b = 0; - } - - return new HunterLab(l, a, b, this.HunterLabWhitePoint); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs deleted file mode 100644 index 4d15cb5d5..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieXyzToLinearRgbConverter.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and -/// -internal sealed class CieXyzToLinearRgbConverter : LinearRgbAndCieXyzConverterBase -{ - private readonly Matrix4x4 conversionMatrix; - - /// - /// Initializes a new instance of the class. - /// - public CieXyzToLinearRgbConverter() - : this(Rgb.DefaultWorkingSpace) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The target working space. - public CieXyzToLinearRgbConverter(RgbWorkingSpace workingSpace) - { - this.TargetWorkingSpace = workingSpace; - - // Gets the inverted Rgb -> Xyz matrix - Matrix4x4.Invert(GetRgbToCieXyzMatrix(workingSpace), out Matrix4x4 inverted); - - this.conversionMatrix = inverted; - } - - /// - /// Gets the target working space. - /// - public RgbWorkingSpace TargetWorkingSpace { get; } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result. - [MethodImpl(InliningOptions.ShortMethod)] - public LinearRgb Convert(in CieXyz input) - { - var vector = Vector3.Transform(input.ToVector3(), this.conversionMatrix); - - return new LinearRgb(vector, this.TargetWorkingSpace); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs deleted file mode 100644 index 07ca1c7e4..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CmykAndRgbConverter.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and . -/// -internal static class CmykAndRgbConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static Rgb Convert(in Cmyk input) - { - Vector3 rgb = (Vector3.One - new Vector3(input.C, input.M, input.Y)) * (Vector3.One - new Vector3(input.K)); - return new Rgb(rgb); - } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result. - [MethodImpl(InliningOptions.ShortMethod)] - public static Cmyk Convert(in Rgb input) - { - // To CMY - Vector3 cmy = Vector3.One - input.ToVector3(); - - // To CMYK - Vector3 k = new(MathF.Min(cmy.X, MathF.Min(cmy.Y, cmy.Z))); - - if (MathF.Abs(k.X - 1F) < Constants.Epsilon) - { - return new Cmyk(0, 0, 0, 1F); - } - - cmy = (cmy - k) / (Vector3.One - k); - - return new Cmyk(cmy.X, cmy.Y, cmy.Z, k.X); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs deleted file mode 100644 index 24aecc3c4..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HslAndRgbConverter.cs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between HSL and Rgb -/// See for formulas. -/// -internal static class HslAndRgbConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static Rgb Convert(in Hsl input) - { - float rangedH = input.H / 360F; - float r = 0; - float g = 0; - float b = 0; - float s = input.S; - float l = input.L; - - if (MathF.Abs(l) > Constants.Epsilon) - { - if (MathF.Abs(s) < Constants.Epsilon) - { - r = g = b = l; - } - else - { - float temp2 = (l < .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 Rgb(r, g, b); - } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static Hsl Convert(in Rgb input) - { - float r = input.R; - float g = input.G; - float b = input.B; - - float max = MathF.Max(r, MathF.Max(g, b)); - float min = MathF.Min(r, MathF.Min(g, b)); - float chroma = max - min; - float h = 0F; - float s = 0F; - float l = (max + min) / 2F; - - if (MathF.Abs(chroma) < Constants.Epsilon) - { - return new Hsl(0F, s, l); - } - - if (MathF.Abs(r - max) < Constants.Epsilon) - { - h = (g - b) / chroma; - } - else if (MathF.Abs(g - max) < Constants.Epsilon) - { - h = 2F + ((b - r) / chroma); - } - else if (MathF.Abs(b - max) < Constants.Epsilon) - { - h = 4F + ((r - g) / chroma); - } - - h *= 60F; - if (h < 0F) - { - h += 360F; - } - - if (l <= .5F) - { - s = chroma / (max + min); - } - else - { - s = chroma / (2F - max - min); - } - - return new Hsl(h, s, l); - } - - /// - /// Gets the color component from the given values. - /// - /// The first value. - /// The second value. - /// The third value. - /// - /// The . - /// - [MethodImpl(InliningOptions.ShortMethod)] - private static float GetColorComponent(float first, float second, float third) - { - third = MoveIntoRange(third); - if (third < 0.1666667F) - { - return first + ((second - first) * 6F * third); - } - - if (third < .5F) - { - return second; - } - - if (third < 0.6666667F) - { - return first + ((second - first) * (0.6666667F - third) * 6F); - } - - return first; - } - - /// - /// Moves the specific value within the acceptable range for - /// conversion. - /// Used for converting colors to this type. - /// - /// The value to shift. - /// - /// The . - /// - [MethodImpl(InliningOptions.ShortMethod)] - private static float MoveIntoRange(float value) - { - if (value < 0F) - { - value++; - } - else if (value > 1F) - { - value--; - } - - return value; - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs deleted file mode 100644 index 527357617..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HsvAndRgbConverter.cs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between HSV and Rgb -/// See for formulas. -/// -internal static class HsvAndRgbConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static Rgb Convert(in Hsv input) - { - float s = input.S; - float v = input.V; - - if (MathF.Abs(s) < Constants.Epsilon) - { - return new Rgb(v, v, v); - } - - float h = (MathF.Abs(input.H - 360) < Constants.Epsilon) ? 0 : input.H / 60; - int i = (int)Math.Truncate(h); - float f = h - i; - - float p = v * (1F - s); - float q = v * (1F - (s * f)); - float t = v * (1F - (s * (1F - 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 Rgb(r, g, b); - } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static Hsv Convert(in Rgb input) - { - float r = input.R; - float g = input.G; - float b = input.B; - - float max = MathF.Max(r, MathF.Max(g, b)); - float min = MathF.Min(r, MathF.Min(g, b)); - float chroma = max - min; - float h = 0; - float s = 0; - float v = max; - - if (MathF.Abs(chroma) < Constants.Epsilon) - { - return new Hsv(0, s, v); - } - - if (MathF.Abs(r - max) < Constants.Epsilon) - { - h = (g - b) / chroma; - } - else if (MathF.Abs(g - max) < Constants.Epsilon) - { - h = 2 + ((b - r) / chroma); - } - else if (MathF.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); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs deleted file mode 100644 index 3930e8dc2..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and -/// -internal sealed class HunterLabToCieXyzConverter : CieXyzAndHunterLabConverterBase -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public static CieXyz Convert(in HunterLab input) - { - // Conversion algorithm described here: http://en.wikipedia.org/wiki/Lab_color_space#Hunter_Lab - float l = input.L, a = input.A, b = input.B; - float xn = input.WhitePoint.X, yn = input.WhitePoint.Y, zn = input.WhitePoint.Z; - - float ka = ComputeKa(input.WhitePoint); - float kb = ComputeKb(input.WhitePoint); - - float pow = Numerics.Pow2(l / 100F); - float sqrtPow = MathF.Sqrt(pow); - float y = pow * yn; - - float x = (((a / ka) * sqrtPow) + pow) * xn; - float z = (((b / kb) * sqrtPow) - pow) * (-zn); - - return new CieXyz(x, y, z); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs deleted file mode 100644 index 27391fc80..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbAndCieXyzConverterBase.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Provides base methods for converting between and color spaces. -/// -internal abstract class LinearRgbAndCieXyzConverterBase -{ - /// - /// Returns the correct matrix to convert between the Rgb and CieXyz color space. - /// - /// The Rgb working space. - /// The based on the chromaticity and working space. - public static Matrix4x4 GetRgbToCieXyzMatrix(RgbWorkingSpace workingSpace) - { - DebugGuard.NotNull(workingSpace, nameof(workingSpace)); - RgbPrimariesChromaticityCoordinates chromaticity = workingSpace.ChromaticityCoordinates; - - float xr = chromaticity.R.X; - float xg = chromaticity.G.X; - float xb = chromaticity.B.X; - float yr = chromaticity.R.Y; - float yg = chromaticity.G.Y; - float yb = chromaticity.B.Y; - - float mXr = xr / yr; - float mZr = (1 - xr - yr) / yr; - - float mXg = xg / yg; - float mZg = (1 - xg - yg) / yg; - - float mXb = xb / yb; - float mZb = (1 - xb - yb) / yb; - - Matrix4x4 xyzMatrix = new() - { - M11 = mXr, - M21 = mXg, - M31 = mXb, - M12 = 1F, - M22 = 1F, - M32 = 1F, - M13 = mZr, - M23 = mZg, - M33 = mZb, - M44 = 1F - }; - - Matrix4x4.Invert(xyzMatrix, out Matrix4x4 inverseXyzMatrix); - - Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.ToVector3(), inverseXyzMatrix); - - // Use transposed Rows/Columns - // TODO: Is there a built in method for this multiplication? - return new Matrix4x4 - { - M11 = vector.X * mXr, - M21 = vector.Y * mXg, - M31 = vector.Z * mXb, - M12 = vector.X * 1, - M22 = vector.Y * 1, - M32 = vector.Z * 1, - M13 = vector.X * mZr, - M23 = vector.Y * mZg, - M33 = vector.Z * mZb, - M44 = 1F - }; - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs deleted file mode 100644 index 091cab993..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToCieXyzConverter.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and -/// -internal sealed class LinearRgbToCieXyzConverter : LinearRgbAndCieXyzConverterBase -{ - private readonly Matrix4x4 conversionMatrix; - - /// - /// Initializes a new instance of the class. - /// - public LinearRgbToCieXyzConverter() - : this(Rgb.DefaultWorkingSpace) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The target working space. - public LinearRgbToCieXyzConverter(RgbWorkingSpace workingSpace) - { - this.SourceWorkingSpace = workingSpace; - this.conversionMatrix = GetRgbToCieXyzMatrix(workingSpace); - } - - /// - /// Gets the source working space - /// - public RgbWorkingSpace SourceWorkingSpace { get; } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result - [MethodImpl(InliningOptions.ShortMethod)] - public CieXyz Convert(in LinearRgb input) - { - DebugGuard.IsTrue(input.WorkingSpace.Equals(this.SourceWorkingSpace), nameof(input.WorkingSpace), "Input and source working spaces must be equal."); - - Vector3 vector = Vector3.Transform(input.ToVector3(), this.conversionMatrix); - return new CieXyz(vector); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs deleted file mode 100644 index d41e7f74d..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/LinearRgbToRgbConverter.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and . -/// -internal static class LinearRgbToRgbConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result. - [MethodImpl(InliningOptions.ShortMethod)] - public static Rgb Convert(in LinearRgb input) => - new( - r: input.WorkingSpace.Compress(input.R), - g: input.WorkingSpace.Compress(input.G), - b: input.WorkingSpace.Compress(input.B), - workingSpace: input.WorkingSpace); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs deleted file mode 100644 index c63bbae3d..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/RgbToLinearRgbConverter.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between Rgb and LinearRgb. -/// -internal static class RgbToLinearRgbConverter -{ - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result. - [MethodImpl(InliningOptions.ShortMethod)] - public static LinearRgb Convert(in Rgb input) - => new( - r: input.WorkingSpace.Expand(input.R), - g: input.WorkingSpace.Expand(input.G), - b: input.WorkingSpace.Expand(input.B), - workingSpace: input.WorkingSpace); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs deleted file mode 100644 index eda55ec4c..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/YCbCrAndRgbConverter.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Color converter between and -/// See for formulas. -/// -internal static class YCbCrAndRgbConverter -{ - private static readonly Vector3 MaxBytes = new(255F); - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result. - [MethodImpl(InliningOptions.ShortMethod)] - public static Rgb Convert(in YCbCr input) - { - float y = input.Y; - float cb = input.Cb - 128F; - float cr = input.Cr - 128F; - - float r = MathF.Round(y + (1.402F * cr), MidpointRounding.AwayFromZero); - float g = MathF.Round(y - (0.344136F * cb) - (0.714136F * cr), MidpointRounding.AwayFromZero); - float b = MathF.Round(y + (1.772F * cb), MidpointRounding.AwayFromZero); - - return new Rgb(new Vector3(r, g, b) / MaxBytes); - } - - /// - /// Performs the conversion from the input to an instance of type. - /// - /// The input color instance. - /// The converted result. - [MethodImpl(InliningOptions.ShortMethod)] - public static YCbCr Convert(in Rgb input) - { - Vector3 rgb = input.ToVector3() * MaxBytes; - float r = rgb.X; - float g = rgb.Y; - float b = rgb.Z; - - float y = (0.299F * r) + (0.587F * g) + (0.114F * b); - float cb = 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b)); - float cr = 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b)); - - return new YCbCr(y, cb, cr); - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/IChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/IChromaticAdaptation.cs deleted file mode 100644 index 3b6abb041..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/IChromaticAdaptation.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// 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). -/// -public interface IChromaticAdaptation -{ - /// - /// Performs a linear transformation of a source color in to the destination color. - /// - /// Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates). - /// The source color. - /// The source white point. - /// The destination white point. - /// The - CieXyz Transform(in CieXyz source, in CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint); - - /// - /// Performs a bulk linear transformation of a source color in to the destination color. - /// - /// Doesn't crop the resulting color space coordinates (e. g. allows negative values for XYZ coordinates). - /// The span to the source colors. - /// The span to the destination colors. - /// The source white point. - /// The destination white point. - void Transform( - ReadOnlySpan source, - Span destination, - CieXyz sourceWhitePoint, - in CieXyz destinationWhitePoint); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs deleted file mode 100644 index 80bd160e8..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/LmsAdaptationMatrix.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Matrices used for transformation from to , defining the cone response domain. -/// Used in -/// -/// -/// Matrix 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 -/// https://web.stanford.edu/~sujason/ColorBalancing/Papers/Two%20New%20von%20Kries%20Based%20Chromatic%20Adaptation.pdf -/// -public static class LmsAdaptationMatrix -{ - /// - /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez adjusted for D65) - /// - public static readonly Matrix4x4 VonKriesHPEAdjusted - = Matrix4x4.Transpose(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. - }); - - /// - /// Von Kries chromatic adaptation transform matrix (Hunt-Pointer-Estevez for equal energy) - /// - public static readonly Matrix4x4 VonKriesHPE - = Matrix4x4.Transpose(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 - }); - - /// - /// XYZ scaling chromatic adaptation transform matrix - /// - public static readonly Matrix4x4 XyzScaling = Matrix4x4.Transpose(Matrix4x4.Identity); - - /// - /// Bradford chromatic adaptation transform matrix (used in CMCCAT97) - /// - public static readonly Matrix4x4 Bradford - = Matrix4x4.Transpose(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 - }); - - /// - /// Spectral sharpening and the Bradford transform - /// - public static readonly Matrix4x4 BradfordSharp - = Matrix4x4.Transpose(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 - }); - - /// - /// CMCCAT2000 (fitted from all available color data sets) - /// - public static readonly Matrix4x4 CMCCAT2000 - = Matrix4x4.Transpose(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 - }); - - /// - /// CAT02 (optimized for minimizing CIELAB differences) - /// - public static readonly Matrix4x4 CAT02 - = Matrix4x4.Transpose(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 - }); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs deleted file mode 100644 index 625e6c551..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/RGBPrimariesChromaticityCoordinates.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Represents the chromaticity coordinates of RGB primaries. -/// One of the specifiers of . -/// -public readonly struct RgbPrimariesChromaticityCoordinates : IEquatable -{ - /// - /// Initializes a new instance of the struct. - /// - /// The chromaticity coordinates of the red channel. - /// The chromaticity coordinates of the green channel. - /// The chromaticity coordinates of the blue channel. - public RgbPrimariesChromaticityCoordinates(CieXyChromaticityCoordinates r, CieXyChromaticityCoordinates g, CieXyChromaticityCoordinates b) - { - this.R = r; - this.G = g; - this.B = b; - } - - /// - /// Gets the chromaticity coordinates of the red channel. - /// - public CieXyChromaticityCoordinates R { get; } - - /// - /// Gets the chromaticity coordinates of the green channel. - /// - public CieXyChromaticityCoordinates G { get; } - - /// - /// Gets the chromaticity coordinates of the blue channel. - /// - public CieXyChromaticityCoordinates B { get; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - public static bool operator ==(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right) - { - return left.Equals(right); - } - - /// - /// Compares two objects for inequality - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - public static bool operator !=(RgbPrimariesChromaticityCoordinates left, RgbPrimariesChromaticityCoordinates right) - { - return !left.Equals(right); - } - - /// - public override bool Equals(object? obj) - { - return obj is RgbPrimariesChromaticityCoordinates other && this.Equals(other); - } - - /// - public bool Equals(RgbPrimariesChromaticityCoordinates other) - { - return this.R.Equals(other.R) && this.G.Equals(other.G) && this.B.Equals(other.B); - } - - /// - public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs deleted file mode 100644 index 8f33e59f5..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/VonKriesChromaticAdaptation.cs +++ /dev/null @@ -1,99 +0,0 @@ -// 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.ColorSpaces.Conversion; - -/// -/// Implementation of the Von Kries chromatic adaptation model. -/// -/// -/// Transformation described here: -/// http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html -/// -public sealed class VonKriesChromaticAdaptation : IChromaticAdaptation -{ - private readonly CieXyzAndLmsConverter converter; - - /// - /// Initializes a new instance of the class. - /// - public VonKriesChromaticAdaptation() - : this(new CieXyzAndLmsConverter()) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The transformation matrix used for the conversion (definition of the cone response domain). - /// - /// - public VonKriesChromaticAdaptation(Matrix4x4 transformationMatrix) - : this(new CieXyzAndLmsConverter(transformationMatrix)) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The color converter - internal VonKriesChromaticAdaptation(CieXyzAndLmsConverter converter) => this.converter = converter; - - /// - public CieXyz Transform(in CieXyz source, in CieXyz sourceWhitePoint, in CieXyz destinationWhitePoint) - { - if (sourceWhitePoint.Equals(destinationWhitePoint)) - { - return source; - } - - Lms sourceColorLms = this.converter.Convert(source); - Lms sourceWhitePointLms = this.converter.Convert(sourceWhitePoint); - Lms targetWhitePointLms = this.converter.Convert(destinationWhitePoint); - - Vector3 vector = targetWhitePointLms.ToVector3() / sourceWhitePointLms.ToVector3(); - var targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.ToVector3())); - - return this.converter.Convert(targetColorLms); - } - - /// - public void Transform( - ReadOnlySpan source, - Span destination, - CieXyz sourceWhitePoint, - in CieXyz destinationWhitePoint) - { - Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); - int count = source.Length; - - if (sourceWhitePoint.Equals(destinationWhitePoint)) - { - source.CopyTo(destination[..count]); - return; - } - - ref CieXyz sourceRef = ref MemoryMarshal.GetReference(source); - ref CieXyz destRef = ref MemoryMarshal.GetReference(destination); - - for (nuint i = 0; i < (uint)count; i++) - { - ref CieXyz sp = ref Unsafe.Add(ref sourceRef, i); - ref CieXyz dp = ref Unsafe.Add(ref destRef, i); - - Lms sourceColorLms = this.converter.Convert(sp); - Lms sourceWhitePointLms = this.converter.Convert(sourceWhitePoint); - Lms targetWhitePointLms = this.converter.Convert(destinationWhitePoint); - - Vector3 vector = targetWhitePointLms.ToVector3() / sourceWhitePointLms.ToVector3(); - var targetColorLms = new Lms(Vector3.Multiply(vector, sourceColorLms.ToVector3())); - - dp = this.converter.Convert(targetColorLms); - } - } -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs deleted file mode 100644 index e95f1f2b4..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/GammaWorkingSpace.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// The gamma working space. -/// -public sealed class GammaWorkingSpace : RgbWorkingSpace -{ - /// - /// Initializes a new instance of the class. - /// - /// The gamma value. - /// The reference white point. - /// The chromaticity of the rgb primaries. - public GammaWorkingSpace(float gamma, CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - : base(referenceWhite, chromaticityCoordinates) => this.Gamma = gamma; - - /// - /// Gets the gamma value. - /// - public float Gamma { get; } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Compress(float channel) => GammaCompanding.Compress(channel, this.Gamma); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Expand(float channel) => GammaCompanding.Expand(channel, this.Gamma); - - /// - public override bool Equals(object? obj) - { - if (obj is null) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is GammaWorkingSpace other) - { - return this.Gamma.Equals(other.Gamma) - && this.WhitePoint.Equals(other.WhitePoint) - && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); - } - - return false; - } - - /// - public override int GetHashCode() => HashCode.Combine( - this.WhitePoint, - this.ChromaticityCoordinates, - this.Gamma); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs deleted file mode 100644 index 199f6c8d8..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/LWorkingSpace.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// L* working space. -/// -public sealed class LWorkingSpace : RgbWorkingSpace -{ - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The chromaticity of the rgb primaries. - public LWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - : base(referenceWhite, chromaticityCoordinates) - { - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Compress(float channel) => LCompanding.Compress(channel); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Expand(float channel) => LCompanding.Expand(channel); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs deleted file mode 100644 index 52cc0f95a..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec2020WorkingSpace.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. -/// -public sealed class Rec2020WorkingSpace : RgbWorkingSpace -{ - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The chromaticity of the rgb primaries. - public Rec2020WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - : base(referenceWhite, chromaticityCoordinates) - { - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Compress(float channel) => Rec2020Companding.Compress(channel); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Expand(float channel) => Rec2020Companding.Expand(channel); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs deleted file mode 100644 index c030e9102..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/Rec709WorkingSpace.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Rec. 709 (ITU-R Recommendation BT.709) working space. -/// -public sealed class Rec709WorkingSpace : RgbWorkingSpace -{ - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The chromaticity of the rgb primaries. - public Rec709WorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - : base(referenceWhite, chromaticityCoordinates) - { - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Compress(float channel) => Rec709Companding.Compress(channel); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Expand(float channel) => Rec709Companding.Expand(channel); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs deleted file mode 100644 index dd1dc62a8..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/RgbWorkingSpace.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// Base class for all implementations of . -/// -public abstract class RgbWorkingSpace -{ - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The chromaticity of the rgb primaries. - protected RgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - { - this.WhitePoint = referenceWhite; - this.ChromaticityCoordinates = chromaticityCoordinates; - } - - /// - /// Gets the reference white point - /// - public CieXyz WhitePoint { get; } - - /// - /// Gets the chromaticity of the rgb primaries. - /// - public RgbPrimariesChromaticityCoordinates ChromaticityCoordinates { get; } - - /// - /// Expands a companded channel to its linear equivalent with respect to the energy. - /// - /// - /// For more info see: - /// - /// - /// The channel value. - /// The representing the linear channel value. - public abstract float Expand(float channel); - - /// - /// Compresses an uncompanded channel (linear) to its nonlinear equivalent (depends on the RGB color system). - /// - /// - /// For more info see: - /// - /// - /// The channel value. - /// The representing the nonlinear channel value. - public abstract float Compress(float channel); - - /// - public override bool Equals(object? obj) - { - if (obj is null) - { - return false; - } - - if (ReferenceEquals(this, obj)) - { - return true; - } - - if (obj is RgbWorkingSpace other) - { - return this.WhitePoint.Equals(other.WhitePoint) - && this.ChromaticityCoordinates.Equals(other.ChromaticityCoordinates); - } - - return false; - } - - /// - public override int GetHashCode() - => HashCode.Combine(this.WhitePoint, this.ChromaticityCoordinates); -} diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs deleted file mode 100644 index 767157f4c..000000000 --- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/WorkingSpaces/SRgbWorkingSpace.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Companding; - -namespace SixLabors.ImageSharp.ColorSpaces.Conversion; - -/// -/// The sRgb working space. -/// -public sealed class SRgbWorkingSpace : RgbWorkingSpace -{ - /// - /// Initializes a new instance of the class. - /// - /// The reference white point. - /// The chromaticity of the rgb primaries. - public SRgbWorkingSpace(CieXyz referenceWhite, RgbPrimariesChromaticityCoordinates chromaticityCoordinates) - : base(referenceWhite, chromaticityCoordinates) - { - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Compress(float channel) => SRgbCompanding.Compress(channel); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override float Expand(float channel) => SRgbCompanding.Expand(channel); -} diff --git a/src/ImageSharp/ColorSpaces/Hsl.cs b/src/ImageSharp/ColorSpaces/Hsl.cs deleted file mode 100644 index cf18c70c7..000000000 --- a/src/ImageSharp/ColorSpaces/Hsl.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents a Hsl (hue, saturation, lightness) color. -/// -public readonly struct Hsl : IEquatable -{ - private static readonly Vector3 Min = Vector3.Zero; - private static readonly Vector3 Max = new(360, 1, 1); - - /// - /// Initializes a new instance of the struct. - /// - /// The h hue component. - /// The s saturation component. - /// The l value (lightness) component. - [MethodImpl(InliningOptions.ShortMethod)] - public Hsl(float h, float s, float l) - : this(new Vector3(h, s, l)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the h, s, l components. - [MethodImpl(InliningOptions.ShortMethod)] - public Hsl(Vector3 vector) - { - vector = Vector3.Clamp(vector, Min, Max); - this.H = vector.X; - this.S = vector.Y; - this.L = vector.Z; - } - - /// - /// Gets the hue component. - /// A value ranging between 0 and 360. - /// - public readonly float H { get; } - - /// - /// Gets the saturation component. - /// A value ranging between 0 and 1. - /// - public readonly float S { get; } - - /// - /// Gets the lightness component. - /// A value ranging between 0 and 1. - /// - public readonly float L { get; } - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(Hsl left, Hsl right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(Hsl left, Hsl right) => !left.Equals(right); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.L); - - /// - public override string ToString() => FormattableString.Invariant($"Hsl({this.H:#0.##}, {this.S:#0.##}, {this.L:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is Hsl other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(Hsl other) - => this.H.Equals(other.H) - && this.S.Equals(other.S) - && this.L.Equals(other.L); -} diff --git a/src/ImageSharp/ColorSpaces/Hsv.cs b/src/ImageSharp/ColorSpaces/Hsv.cs deleted file mode 100644 index 87c16c0b6..000000000 --- a/src/ImageSharp/ColorSpaces/Hsv.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness). -/// -public readonly struct Hsv : IEquatable -{ - private static readonly Vector3 Min = Vector3.Zero; - private static readonly Vector3 Max = new(360, 1, 1); - - /// - /// Initializes a new instance of the struct. - /// - /// The h hue component. - /// The s saturation component. - /// The v value (brightness) component. - [MethodImpl(InliningOptions.ShortMethod)] - public Hsv(float h, float s, float v) - : this(new Vector3(h, s, v)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the h, s, v components. - [MethodImpl(InliningOptions.ShortMethod)] - public Hsv(Vector3 vector) - { - vector = Vector3.Clamp(vector, Min, Max); - this.H = vector.X; - this.S = vector.Y; - this.V = vector.Z; - } - - /// - /// Gets the hue component. - /// A value ranging between 0 and 360. - /// - public readonly float H { get; } - - /// - /// Gets the saturation component. - /// A value ranging between 0 and 1. - /// - public readonly float S { get; } - - /// - /// Gets the value (brightness) component. - /// A value ranging between 0 and 1. - /// - public readonly float V { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(Hsv left, Hsv right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(Hsv left, Hsv right) => !left.Equals(right); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() => HashCode.Combine(this.H, this.S, this.V); - - /// - public override string ToString() => FormattableString.Invariant($"Hsv({this.H:#0.##}, {this.S:#0.##}, {this.V:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is Hsv other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(Hsv other) - => this.H.Equals(other.H) - && this.S.Equals(other.S) - && this.V.Equals(other.V); -} diff --git a/src/ImageSharp/ColorSpaces/HunterLab.cs b/src/ImageSharp/ColorSpaces/HunterLab.cs deleted file mode 100644 index 516574b09..000000000 --- a/src/ImageSharp/ColorSpaces/HunterLab.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents an Hunter LAB color. -/// . -/// -public readonly struct HunterLab : IEquatable -{ - /// - /// D50 standard illuminant. - /// Used when reference white is not specified explicitly. - /// - public static readonly CieXyz DefaultWhitePoint = Illuminants.C; - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The a (green - magenta) component. - /// The b (blue - yellow) component. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public HunterLab(float l, float a, float b) - : this(new Vector3(l, a, b), DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The lightness dimension. - /// The a (green - magenta) component. - /// The b (blue - yellow) component. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public HunterLab(float l, float a, float b, CieXyz whitePoint) - : this(new Vector3(l, a, b), whitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, a, b components. - /// Uses as white point. - [MethodImpl(InliningOptions.ShortMethod)] - public HunterLab(Vector3 vector) - : this(vector, DefaultWhitePoint) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l a b components. - /// The reference white point. - [MethodImpl(InliningOptions.ShortMethod)] - public HunterLab(Vector3 vector, CieXyz whitePoint) - { - // Not clamping as documentation about this space only indicates "usual" ranges - this.L = vector.X; - this.A = vector.Y; - this.B = vector.Z; - this.WhitePoint = whitePoint; - } - - /// - /// Gets the lightness dimension. - /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white). - /// - public readonly float L { get; } - - /// - /// Gets the a color component. - /// A value usually ranging from -100 to 100. Negative is green, positive magenta. - /// - public readonly float A { get; } - - /// - /// Gets the b color component. - /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow - /// - public readonly float B { get; } - - /// - /// Gets the reference white point of this color. - /// - public readonly CieXyz WhitePoint { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - public static bool operator ==(HunterLab left, HunterLab right) => left.Equals(right); - - /// - /// Compares two objects for inequality - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(HunterLab left, HunterLab right) => !left.Equals(right); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B, this.WhitePoint); - - /// - public override string ToString() => FormattableString.Invariant($"HunterLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is HunterLab other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(HunterLab other) - => this.L.Equals(other.L) - && this.A.Equals(other.A) - && this.B.Equals(other.B) - && this.WhitePoint.Equals(other.WhitePoint); -} diff --git a/src/ImageSharp/ColorSpaces/Illuminants.cs b/src/ImageSharp/ColorSpaces/Illuminants.cs deleted file mode 100644 index 7c25305c2..000000000 --- a/src/ImageSharp/ColorSpaces/Illuminants.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// The well known standard illuminants. -/// Standard illuminants provide a basis for comparing images or colors recorded under different lighting -/// -/// -/// Coefficients taken from: http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html -///
-/// Descriptions taken from: http://en.wikipedia.org/wiki/Standard_illuminant -///
-public static class Illuminants -{ - /// - /// Incandescent / Tungsten - /// - public static readonly CieXyz A = new CieXyz(1.09850F, 1F, 0.35585F); - - /// - /// Direct sunlight at noon (obsoleteF) - /// - public static readonly CieXyz B = new CieXyz(0.99072F, 1F, 0.85223F); - - /// - /// Average / North sky Daylight (obsoleteF) - /// - public static readonly CieXyz C = new CieXyz(0.98074F, 1F, 1.18232F); - - /// - /// Horizon Light. ICC profile PCS - /// - public static readonly CieXyz D50 = new CieXyz(0.96422F, 1F, 0.82521F); - - /// - /// Mid-morning / Mid-afternoon Daylight - /// - public static readonly CieXyz D55 = new CieXyz(0.95682F, 1F, 0.92149F); - - /// - /// Noon Daylight: TelevisionF, sRGB color space - /// - public static readonly CieXyz D65 = new CieXyz(0.95047F, 1F, 1.08883F); - - /// - /// North sky Daylight - /// - public static readonly CieXyz D75 = new CieXyz(0.94972F, 1F, 1.22638F); - - /// - /// Equal energy - /// - public static readonly CieXyz E = new CieXyz(1F, 1F, 1F); - - /// - /// Cool White Fluorescent - /// - public static readonly CieXyz F2 = new CieXyz(0.99186F, 1F, 0.67393F); - - /// - /// D65 simulatorF, Daylight simulator - /// - public static readonly CieXyz F7 = new CieXyz(0.95041F, 1F, 1.08747F); - - /// - /// Philips TL84F, Ultralume 40 - /// - public static readonly CieXyz F11 = new CieXyz(1.00962F, 1F, 0.64350F); -} diff --git a/src/ImageSharp/ColorSpaces/LinearRgb.cs b/src/ImageSharp/ColorSpaces/LinearRgb.cs deleted file mode 100644 index 49c7814a0..000000000 --- a/src/ImageSharp/ColorSpaces/LinearRgb.cs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents an linear Rgb color with specified working space -/// -public readonly struct LinearRgb : IEquatable -{ - private static readonly Vector3 Min = Vector3.Zero; - private static readonly Vector3 Max = Vector3.One; - - /// - /// The default LinearRgb working space. - /// - public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; - - /// - /// Initializes a new instance of the struct. - /// - /// The red component ranging between 0 and 1. - /// The green component ranging between 0 and 1. - /// The blue component ranging between 0 and 1. - [MethodImpl(InliningOptions.ShortMethod)] - public LinearRgb(float r, float g, float b) - : this(r, g, b, DefaultWorkingSpace) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The red component ranging between 0 and 1. - /// The green component ranging between 0 and 1. - /// The blue component ranging between 0 and 1. - /// The rgb working space. - [MethodImpl(InliningOptions.ShortMethod)] - public LinearRgb(float r, float g, float b, RgbWorkingSpace workingSpace) - : this(new Vector3(r, g, b), workingSpace) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the r, g, b components. - [MethodImpl(InliningOptions.ShortMethod)] - public LinearRgb(Vector3 vector) - : this(vector, DefaultWorkingSpace) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the r, g, b components. - /// The LinearRgb working space. - [MethodImpl(InliningOptions.ShortMethod)] - public LinearRgb(Vector3 vector, RgbWorkingSpace workingSpace) - { - // Clamp to 0-1 range. - vector = Vector3.Clamp(vector, Min, Max); - this.R = vector.X; - this.G = vector.Y; - this.B = vector.Z; - this.WorkingSpace = workingSpace; - } - - /// - /// Gets the red component. - /// A value usually ranging between 0 and 1. - /// - public readonly float R { get; } - - /// - /// Gets the green component. - /// A value usually ranging between 0 and 1. - /// - public readonly float G { get; } - - /// - /// Gets the blue component. - /// A value usually ranging between 0 and 1. - /// - public readonly float B { get; } - - /// - /// Gets the LinearRgb color space - /// - public readonly RgbWorkingSpace WorkingSpace { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(LinearRgb left, LinearRgb right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(LinearRgb left, LinearRgb right) => !left.Equals(right); - - /// - /// Returns a new representing this instance. - /// - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public Vector3 ToVector3() => new(this.R, this.G, this.B); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); - - /// - public override string ToString() => FormattableString.Invariant($"LinearRgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is LinearRgb other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(LinearRgb other) - => this.R.Equals(other.R) - && this.G.Equals(other.G) - && this.B.Equals(other.B); -} diff --git a/src/ImageSharp/ColorSpaces/Lms.cs b/src/ImageSharp/ColorSpaces/Lms.cs deleted file mode 100644 index 99210ff6e..000000000 --- a/src/ImageSharp/ColorSpaces/Lms.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// 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. -/// -/// -public readonly struct Lms : IEquatable -{ - /// - /// Initializes a new instance of the struct. - /// - /// L represents the responsivity at long wavelengths. - /// M represents the responsivity at medium wavelengths. - /// S represents the responsivity at short wavelengths. - [MethodImpl(InliningOptions.ShortMethod)] - public Lms(float l, float m, float s) - : this(new Vector3(l, m, s)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the l, m, s components. - [MethodImpl(InliningOptions.ShortMethod)] - public Lms(Vector3 vector) - { - // Not clamping as documentation about this space only indicates "usual" ranges - this.L = vector.X; - this.M = vector.Y; - this.S = vector.Z; - } - - /// - /// Gets the L long component. - /// A value usually ranging between -1 and 1. - /// - public readonly float L { get; } - - /// - /// Gets the M medium component. - /// A value usually ranging between -1 and 1. - /// - public readonly float M { get; } - - /// - /// Gets the S short component. - /// A value usually ranging between -1 and 1. - /// - public readonly float S { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(Lms left, Lms right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(Lms left, Lms right) => !left.Equals(right); - - /// - /// Returns a new representing this instance. - /// - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public Vector3 ToVector3() => new(this.L, this.M, this.S); - - /// - public override int GetHashCode() => HashCode.Combine(this.L, this.M, this.S); - - /// - public override string ToString() => FormattableString.Invariant($"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is Lms other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(Lms other) - => this.L.Equals(other.L) - && this.M.Equals(other.M) - && this.S.Equals(other.S); -} diff --git a/src/ImageSharp/ColorSpaces/Rgb.cs b/src/ImageSharp/ColorSpaces/Rgb.cs deleted file mode 100644 index 55052a710..000000000 --- a/src/ImageSharp/ColorSpaces/Rgb.cs +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.ColorSpaces.Conversion; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents an RGB color with specified working space. -/// -public readonly struct Rgb : IEquatable -{ - /// - /// The default rgb working space. - /// - public static readonly RgbWorkingSpace DefaultWorkingSpace = RgbWorkingSpaces.SRgb; - - private static readonly Vector3 Min = Vector3.Zero; - private static readonly Vector3 Max = Vector3.One; - - /// - /// Initializes a new instance of the struct. - /// - /// The red component ranging between 0 and 1. - /// The green component ranging between 0 and 1. - /// The blue component ranging between 0 and 1. - [MethodImpl(InliningOptions.ShortMethod)] - public Rgb(float r, float g, float b) - : this(r, g, b, DefaultWorkingSpace) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The red component ranging between 0 and 1. - /// The green component ranging between 0 and 1. - /// The blue component ranging between 0 and 1. - /// The rgb working space. - [MethodImpl(InliningOptions.ShortMethod)] - public Rgb(float r, float g, float b, RgbWorkingSpace workingSpace) - : this(new Vector3(r, g, b), workingSpace) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the r, g, b components. - [MethodImpl(InliningOptions.ShortMethod)] - public Rgb(Vector3 vector) - : this(vector, DefaultWorkingSpace) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the r, g, b components. - /// The rgb working space. - [MethodImpl(InliningOptions.ShortMethod)] - public Rgb(Vector3 vector, RgbWorkingSpace workingSpace) - { - vector = Vector3.Clamp(vector, Min, Max); - this.R = vector.X; - this.G = vector.Y; - this.B = vector.Z; - this.WorkingSpace = workingSpace; - } - - /// - /// Gets the red component. - /// A value usually ranging between 0 and 1. - /// - public readonly float R { get; } - - /// - /// Gets the green component. - /// A value usually ranging between 0 and 1. - /// - public readonly float G { get; } - - /// - /// Gets the blue component. - /// A value usually ranging between 0 and 1. - /// - public readonly float B { get; } - - /// - /// Gets the Rgb color space - /// - public readonly RgbWorkingSpace WorkingSpace { get; } - - /// - /// Allows the implicit conversion of an instance of to a - /// . - /// - /// The instance of to convert. - /// An instance of . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Rgb(Rgb24 color) => new(color.R / 255F, color.G / 255F, color.B / 255F); - - /// - /// Allows the implicit conversion of an instance of to a - /// . - /// - /// The instance of to convert. - /// An instance of . - [MethodImpl(InliningOptions.ShortMethod)] - public static implicit operator Rgb(Rgba32 color) => new(color.R / 255F, color.G / 255F, color.B / 255F); - - /// - /// Compares two objects for equality. - /// - /// - /// The on the left side of the operand. - /// - /// - /// The on the right side of the operand. - /// - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator ==(Rgb left, Rgb right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(Rgb left, Rgb right) => !left.Equals(right); - - /// - /// Returns a new representing this instance. - /// - /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public Vector3 ToVector3() => new(this.R, this.G, this.B); - - /// - public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); - - /// - public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is Rgb other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(Rgb other) - => this.R.Equals(other.R) - && this.G.Equals(other.G) - && this.B.Equals(other.B); -} diff --git a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs b/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs deleted file mode 100644 index 53c8c2cf0..000000000 --- a/src/ImageSharp/ColorSpaces/RgbWorkingSpaces.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces.Companding; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Chromaticity coordinates based on: -/// -public static class RgbWorkingSpaces -{ - /// - /// sRgb working space. - /// - /// - /// Uses proper companding function, according to: - /// - /// - public static readonly RgbWorkingSpace SRgb = new SRgbWorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); - - /// - /// Simplified sRgb working space (uses gamma companding instead of ). - /// See also . - /// - public static readonly RgbWorkingSpace SRgbSimplified = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.3000F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); - - /// - /// Rec. 709 (ITU-R Recommendation BT.709) working space. - /// - public static readonly RgbWorkingSpace Rec709 = new Rec709WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.64F, 0.33F), new CieXyChromaticityCoordinates(0.30F, 0.60F), new CieXyChromaticityCoordinates(0.15F, 0.06F))); - - /// - /// Rec. 2020 (ITU-R Recommendation BT.2020F) working space. - /// - public static readonly RgbWorkingSpace Rec2020 = new Rec2020WorkingSpace(Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.708F, 0.292F), new CieXyChromaticityCoordinates(0.170F, 0.797F), new CieXyChromaticityCoordinates(0.131F, 0.046F))); - - /// - /// ECI Rgb v2 working space. - /// - public static readonly RgbWorkingSpace ECIRgbv2 = new LWorkingSpace(Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); - - /// - /// Adobe Rgb (1998) working space. - /// - public static readonly RgbWorkingSpace AdobeRgb1998 = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); - - /// - /// Apple sRgb working space. - /// - public static readonly RgbWorkingSpace ApplesRgb = new GammaWorkingSpace(1.8F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6250F, 0.3400F), new CieXyChromaticityCoordinates(0.2800F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); - - /// - /// Best Rgb working space. - /// - public static readonly RgbWorkingSpace BestRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.2150F, 0.7750F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); - - /// - /// Beta Rgb working space. - /// - public static readonly RgbWorkingSpace BetaRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6888F, 0.3112F), new CieXyChromaticityCoordinates(0.1986F, 0.7551F), new CieXyChromaticityCoordinates(0.1265F, 0.0352F))); - - /// - /// Bruce Rgb working space. - /// - public static readonly RgbWorkingSpace BruceRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2800F, 0.6500F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); - - /// - /// CIE Rgb working space. - /// - public static readonly RgbWorkingSpace CIERgb = new GammaWorkingSpace(2.2F, Illuminants.E, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.2740F, 0.7170F), new CieXyChromaticityCoordinates(0.1670F, 0.0090F))); - - /// - /// ColorMatch Rgb working space. - /// - public static readonly RgbWorkingSpace ColorMatchRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.2950F, 0.6050F), new CieXyChromaticityCoordinates(0.1500F, 0.0750F))); - - /// - /// Don Rgb 4 working space. - /// - public static readonly RgbWorkingSpace DonRgb4 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6960F, 0.3000F), new CieXyChromaticityCoordinates(0.2150F, 0.7650F), new CieXyChromaticityCoordinates(0.1300F, 0.0350F))); - - /// - /// Ekta Space PS5 working space. - /// - public static readonly RgbWorkingSpace EktaSpacePS5 = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6950F, 0.3050F), new CieXyChromaticityCoordinates(0.2600F, 0.7000F), new CieXyChromaticityCoordinates(0.1100F, 0.0050F))); - - /// - /// NTSC Rgb working space. - /// - public static readonly RgbWorkingSpace NTSCRgb = new GammaWorkingSpace(2.2F, Illuminants.C, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6700F, 0.3300F), new CieXyChromaticityCoordinates(0.2100F, 0.7100F), new CieXyChromaticityCoordinates(0.1400F, 0.0800F))); - - /// - /// PAL/SECAM Rgb working space. - /// - public static readonly RgbWorkingSpace PALSECAMRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6400F, 0.3300F), new CieXyChromaticityCoordinates(0.2900F, 0.6000F), new CieXyChromaticityCoordinates(0.1500F, 0.0600F))); - - /// - /// ProPhoto Rgb working space. - /// - public static readonly RgbWorkingSpace ProPhotoRgb = new GammaWorkingSpace(1.8F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7347F, 0.2653F), new CieXyChromaticityCoordinates(0.1596F, 0.8404F), new CieXyChromaticityCoordinates(0.0366F, 0.0001F))); - - /// - /// SMPTE-C Rgb working space. - /// - public static readonly RgbWorkingSpace SMPTECRgb = new GammaWorkingSpace(2.2F, Illuminants.D65, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.6300F, 0.3400F), new CieXyChromaticityCoordinates(0.3100F, 0.5950F), new CieXyChromaticityCoordinates(0.1550F, 0.0700F))); - - /// - /// Wide Gamut Rgb working space. - /// - public static readonly RgbWorkingSpace WideGamutRgb = new GammaWorkingSpace(2.2F, Illuminants.D50, new RgbPrimariesChromaticityCoordinates(new CieXyChromaticityCoordinates(0.7350F, 0.2650F), new CieXyChromaticityCoordinates(0.1150F, 0.8260F), new CieXyChromaticityCoordinates(0.1570F, 0.0180F))); -} diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs deleted file mode 100644 index acb59388f..000000000 --- a/src/ImageSharp/ColorSpaces/YCbCr.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace SixLabors.ImageSharp.ColorSpaces; - -/// -/// Represents an YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification for the JFIF use with Jpeg. -/// -/// -/// -public readonly struct YCbCr : IEquatable -{ - private static readonly Vector3 Min = Vector3.Zero; - private static readonly Vector3 Max = new(255); - - /// - /// Initializes a new instance of the struct. - /// - /// The y luminance component. - /// The cb chroma component. - /// The cr chroma component. - [MethodImpl(InliningOptions.ShortMethod)] - public YCbCr(float y, float cb, float cr) - : this(new Vector3(y, cb, cr)) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The vector representing the y, cb, cr components. - [MethodImpl(InliningOptions.ShortMethod)] - public YCbCr(Vector3 vector) - { - vector = Vector3.Clamp(vector, Min, Max); - this.Y = vector.X; - this.Cb = vector.Y; - this.Cr = vector.Z; - } - - /// - /// Gets the Y luminance component. - /// A value ranging between 0 and 255. - /// - public readonly float Y { get; } - - /// - /// Gets the Cb chroma component. - /// A value ranging between 0 and 255. - /// - public readonly float Cb { get; } - - /// - /// Gets the Cr chroma component. - /// A value ranging between 0 and 255. - /// - public readonly float Cr { get; } - - /// - /// Compares two objects for equality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is equal to the parameter; otherwise, false. - /// - public static bool operator ==(YCbCr left, YCbCr right) => left.Equals(right); - - /// - /// Compares two objects for inequality. - /// - /// The on the left side of the operand. - /// The on the right side of the operand. - /// - /// True if the current left is unequal to the parameter; otherwise, false. - /// - [MethodImpl(InliningOptions.ShortMethod)] - public static bool operator !=(YCbCr left, YCbCr right) => !left.Equals(right); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public override int GetHashCode() => HashCode.Combine(this.Y, this.Cb, this.Cr); - - /// - public override string ToString() => FormattableString.Invariant($"YCbCr({this.Y}, {this.Cb}, {this.Cr})"); - - /// - public override bool Equals(object? obj) => obj is YCbCr other && this.Equals(other); - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public bool Equals(YCbCr other) - => this.Y.Equals(other.Y) - && this.Cb.Equals(other.Cb) - && this.Cr.Equals(other.Cr); -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLabTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLabTests.cs deleted file mode 100644 index 52cb63d21..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieLabTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CieLabTests -{ - [Fact] - public void CieLabConstructorAssignsFields() - { - const float l = 75F; - const float a = -64F; - const float b = 87F; - var cieLab = new CieLab(l, a, b); - - Assert.Equal(l, cieLab.L); - Assert.Equal(a, cieLab.A); - Assert.Equal(b, cieLab.B); - } - - [Fact] - public void CieLabEquality() - { - var x = default(CieLab); - var y = new CieLab(Vector3.One); - - Assert.True(default(CieLab) == default(CieLab)); - Assert.True(new CieLab(1, 0, 1) != default(CieLab)); - Assert.False(new CieLab(1, 0, 1) == default(CieLab)); - Assert.Equal(default(CieLab), default(CieLab)); - Assert.Equal(new CieLab(1, 0, 1), new CieLab(1, 0, 1)); - Assert.Equal(new CieLab(Vector3.One), new CieLab(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(new CieLab(1, 0, 1) == default(CieLab)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLchTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLchTests.cs deleted file mode 100644 index 83e4d8a59..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieLchTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CieLchTests -{ - [Fact] - public void CieLchConstructorAssignsFields() - { - const float l = 75F; - const float c = 64F; - const float h = 287F; - var cieLch = new CieLch(l, c, h); - - Assert.Equal(l, cieLch.L); - Assert.Equal(c, cieLch.C); - Assert.Equal(h, cieLch.H); - } - - [Fact] - public void CieLchEquality() - { - var x = default(CieLch); - var y = new CieLch(Vector3.One); - - Assert.True(default(CieLch) == default(CieLch)); - Assert.False(default(CieLch) != default(CieLch)); - Assert.Equal(default(CieLch), default(CieLch)); - Assert.Equal(new CieLch(1, 0, 1), new CieLch(1, 0, 1)); - Assert.Equal(new CieLch(Vector3.One), new CieLch(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLchuvTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLchuvTests.cs deleted file mode 100644 index f6c68aae9..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieLchuvTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CieLchuvTests -{ - [Fact] - public void CieLchuvConstructorAssignsFields() - { - const float l = 75F; - const float c = 64F; - const float h = 287F; - var cieLchuv = new CieLchuv(l, c, h); - - Assert.Equal(l, cieLchuv.L); - Assert.Equal(c, cieLchuv.C); - Assert.Equal(h, cieLchuv.H); - } - - [Fact] - public void CieLchuvEquality() - { - var x = default(CieLchuv); - var y = new CieLchuv(Vector3.One); - - Assert.True(default(CieLchuv) == default(CieLchuv)); - Assert.False(default(CieLchuv) != default(CieLchuv)); - Assert.Equal(default(CieLchuv), default(CieLchuv)); - Assert.Equal(new CieLchuv(1, 0, 1), new CieLchuv(1, 0, 1)); - Assert.Equal(new CieLchuv(Vector3.One), new CieLchuv(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieLuvTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieLuvTests.cs deleted file mode 100644 index 0ebf1bdea..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieLuvTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CieLuvTests -{ - [Fact] - public void CieLuvConstructorAssignsFields() - { - const float l = 75F; - const float c = -64F; - const float h = 87F; - var cieLuv = new CieLuv(l, c, h); - - Assert.Equal(l, cieLuv.L); - Assert.Equal(c, cieLuv.U); - Assert.Equal(h, cieLuv.V); - } - - [Fact] - public void CieLuvEquality() - { - var x = default(CieLuv); - var y = new CieLuv(Vector3.One); - - Assert.True(default(CieLuv) == default(CieLuv)); - Assert.False(default(CieLuv) != default(CieLuv)); - Assert.Equal(default(CieLuv), default(CieLuv)); - Assert.Equal(new CieLuv(1, 0, 1), new CieLuv(1, 0, 1)); - Assert.Equal(new CieLuv(Vector3.One), new CieLuv(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs deleted file mode 100644 index 061d6c432..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyChromaticityCoordinatesTests.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CieXyChromaticityCoordinatesTests -{ - [Fact] - public void CieXyChromaticityCoordinatesConstructorAssignsFields() - { - const float x = .75F; - const float y = .64F; - var coordinates = new CieXyChromaticityCoordinates(x, y); - - Assert.Equal(x, coordinates.X); - Assert.Equal(y, coordinates.Y); - } - - [Fact] - public void CieXyChromaticityCoordinatesEquality() - { - var x = default(CieXyChromaticityCoordinates); - var y = new CieXyChromaticityCoordinates(1, 1); - - Assert.True(default(CieXyChromaticityCoordinates) == default(CieXyChromaticityCoordinates)); - Assert.True(new CieXyChromaticityCoordinates(1, 0) != default(CieXyChromaticityCoordinates)); - Assert.False(new CieXyChromaticityCoordinates(1, 0) == default(CieXyChromaticityCoordinates)); - Assert.Equal(default(CieXyChromaticityCoordinates), default(CieXyChromaticityCoordinates)); - Assert.Equal(new CieXyChromaticityCoordinates(1, 0), new CieXyChromaticityCoordinates(1, 0)); - Assert.Equal(new CieXyChromaticityCoordinates(1, 1), new CieXyChromaticityCoordinates(1, 1)); - Assert.False(x.Equals(y)); - Assert.False(new CieXyChromaticityCoordinates(1, 0) == default(CieXyChromaticityCoordinates)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyyTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyyTests.cs deleted file mode 100644 index f1eb12640..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyyTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CieXyyTests -{ - [Fact] - public void CieXyyConstructorAssignsFields() - { - const float x = 75F; - const float y = 64F; - const float yl = 287F; - var cieXyy = new CieXyy(x, y, yl); - - Assert.Equal(x, cieXyy.X); - Assert.Equal(y, cieXyy.Y); - Assert.Equal(y, cieXyy.Y); - } - - [Fact] - public void CieXyyEquality() - { - var x = default(CieXyy); - var y = new CieXyy(Vector3.One); - - Assert.True(default(CieXyy) == default(CieXyy)); - Assert.False(default(CieXyy) != default(CieXyy)); - Assert.Equal(default(CieXyy), default(CieXyy)); - Assert.Equal(new CieXyy(1, 0, 1), new CieXyy(1, 0, 1)); - Assert.Equal(new CieXyy(Vector3.One), new CieXyy(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CieXyzTests.cs b/tests/ImageSharp.Tests/Colorspaces/CieXyzTests.cs deleted file mode 100644 index 6de961cf1..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CieXyzTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CieXyzTests -{ - [Fact] - public void CieXyzConstructorAssignsFields() - { - const float x = 75F; - const float y = 64F; - const float z = 287F; - var cieXyz = new CieXyz(x, y, z); - - Assert.Equal(x, cieXyz.X); - Assert.Equal(y, cieXyz.Y); - Assert.Equal(z, cieXyz.Z); - } - - [Fact] - public void CieXyzEquality() - { - var x = default(CieXyz); - var y = new CieXyz(Vector3.One); - - Assert.True(default(CieXyz) == default(CieXyz)); - Assert.False(default(CieXyz) != default(CieXyz)); - Assert.Equal(default(CieXyz), default(CieXyz)); - Assert.Equal(new CieXyz(1, 0, 1), new CieXyz(1, 0, 1)); - Assert.Equal(new CieXyz(Vector3.One), new CieXyz(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/CmykTests.cs b/tests/ImageSharp.Tests/Colorspaces/CmykTests.cs deleted file mode 100644 index b4e55ed24..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/CmykTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class CmykTests -{ - [Fact] - public void CmykConstructorAssignsFields() - { - const float c = .75F; - const float m = .64F; - const float y = .87F; - const float k = .334F; - var cmyk = new Cmyk(c, m, y, k); - - Assert.Equal(c, cmyk.C); - Assert.Equal(m, cmyk.M); - Assert.Equal(y, cmyk.Y); - Assert.Equal(k, cmyk.K); - } - - [Fact] - public void CmykEquality() - { - var x = default(Cmyk); - var y = new Cmyk(Vector4.One); - - Assert.True(default(Cmyk) == default(Cmyk)); - Assert.False(default(Cmyk) != default(Cmyk)); - Assert.Equal(default(Cmyk), default(Cmyk)); - Assert.Equal(new Cmyk(1, 0, 1, 0), new Cmyk(1, 0, 1, 0)); - Assert.Equal(new Cmyk(Vector4.One), new Cmyk(Vector4.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs b/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs deleted file mode 100644 index 3fb359890..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Companding/CompandingTests.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces.Companding; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Companding; - -/// -/// Tests various companding algorithms. Expanded numbers are hand calculated from formulas online. -/// -public class CompandingTests -{ - private static readonly ApproximateFloatComparer FloatComparer = new ApproximateFloatComparer(.00001F); - - [Fact] - public void Rec2020Companding_IsCorrect() - { - const float input = .667F; - float e = Rec2020Companding.Expand(input); - float c = Rec2020Companding.Compress(e); - CompandingIsCorrectImpl(e, c, .4484759F, input); - } - - [Fact] - public void Rec709Companding_IsCorrect() - { - const float input = .667F; - float e = Rec709Companding.Expand(input); - float c = Rec709Companding.Compress(e); - CompandingIsCorrectImpl(e, c, .4483577F, input); - } - - [Fact] - public void SRgbCompanding_IsCorrect() - { - const float input = .667F; - float e = SRgbCompanding.Expand(input); - float c = SRgbCompanding.Compress(e); - CompandingIsCorrectImpl(e, c, .40242353F, input); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(30)] - public void SRgbCompanding_Expand_VectorSpan(int length) - { - var rnd = new Random(42); - Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); - var expected = new Vector4[source.Length]; - - for (int i = 0; i < source.Length; i++) - { - Vector4 s = source[i]; - ref Vector4 e = ref expected[i]; - SRgbCompanding.Expand(ref s); - e = s; - } - - SRgbCompanding.Expand(source); - - Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(30)] - public void SRgbCompanding_Compress_VectorSpan(int length) - { - var rnd = new Random(42); - Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); - var expected = new Vector4[source.Length]; - - for (int i = 0; i < source.Length; i++) - { - Vector4 s = source[i]; - ref Vector4 e = ref expected[i]; - SRgbCompanding.Compress(ref s); - e = s; - } - - SRgbCompanding.Compress(source); - - Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); - } - - [Fact] - public void GammaCompanding_IsCorrect() - { - const float gamma = 2.2F; - const float input = .667F; - float e = GammaCompanding.Expand(input, gamma); - float c = GammaCompanding.Compress(e, gamma); - CompandingIsCorrectImpl(e, c, .41027668F, input); - } - - [Fact] - public void LCompanding_IsCorrect() - { - const float input = .667F; - float e = LCompanding.Expand(input); - float c = LCompanding.Compress(e); - CompandingIsCorrectImpl(e, c, .36236193F, input); - } - - private static void CompandingIsCorrectImpl(float e, float c, float expanded, float compressed) - { - Assert.Equal(expanded, e, FloatComparer); - Assert.Equal(compressed, c, FloatComparer); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs deleted file mode 100644 index d66a73b5a..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/ApproximateColorspaceComparer.cs +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Allows the approximate comparison of colorspace component values. -/// -internal readonly struct ApproximateColorSpaceComparer : - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer, - IEqualityComparer -{ - private readonly float epsilon; - - /// - /// Initializes a new instance of the class. - /// - /// The comparison error difference epsilon to use. - public ApproximateColorSpaceComparer(float epsilon = 1F) => this.epsilon = epsilon; - - /// - public bool Equals(Rgb x, Rgb y) - { - return this.Equals(x.R, y.R) - && this.Equals(x.G, y.G) - && this.Equals(x.B, y.B); - } - - /// - public int GetHashCode(Rgb obj) => obj.GetHashCode(); - - /// - public bool Equals(LinearRgb x, LinearRgb y) - { - return this.Equals(x.R, y.R) - && this.Equals(x.G, y.G) - && this.Equals(x.B, y.B); - } - - /// - public int GetHashCode(LinearRgb obj) => obj.GetHashCode(); - - /// - public bool Equals(CieLab x, CieLab y) - { - return this.Equals(x.L, y.L) - && this.Equals(x.A, y.A) - && this.Equals(x.B, y.B); - } - - /// - public int GetHashCode(CieLab obj) => obj.GetHashCode(); - - /// - public bool Equals(CieLch x, CieLch y) - { - return this.Equals(x.L, y.L) - && this.Equals(x.C, y.C) - && this.Equals(x.H, y.H); - } - - /// - public int GetHashCode(CieLch obj) => obj.GetHashCode(); - - /// - public bool Equals(CieLchuv x, CieLchuv y) - { - return this.Equals(x.L, y.L) - && this.Equals(x.C, y.C) - && this.Equals(x.H, y.H); - } - - /// - public int GetHashCode(CieLchuv obj) => obj.GetHashCode(); - - /// - public bool Equals(CieLuv x, CieLuv y) - { - return this.Equals(x.L, y.L) - && this.Equals(x.U, y.U) - && this.Equals(x.V, y.V); - } - - /// - public int GetHashCode(CieLuv obj) => obj.GetHashCode(); - - /// - public bool Equals(CieXyz x, CieXyz y) - { - return this.Equals(x.X, y.X) - && this.Equals(x.Y, y.Y) - && this.Equals(x.Z, y.Z); - } - - /// - public int GetHashCode(CieXyz obj) => obj.GetHashCode(); - - /// - public bool Equals(CieXyy x, CieXyy y) - { - return this.Equals(x.X, y.X) - && this.Equals(x.Y, y.Y) - && this.Equals(x.Yl, y.Yl); - } - - /// - public int GetHashCode(CieXyy obj) => obj.GetHashCode(); - - /// - public bool Equals(Cmyk x, Cmyk y) - { - return this.Equals(x.C, y.C) - && this.Equals(x.M, y.M) - && this.Equals(x.Y, y.Y) - && this.Equals(x.K, y.K); - } - - /// - public int GetHashCode(Cmyk obj) => obj.GetHashCode(); - - /// - public bool Equals(HunterLab x, HunterLab y) - { - return this.Equals(x.L, y.L) - && this.Equals(x.A, y.A) - && this.Equals(x.B, y.B); - } - - /// - public int GetHashCode(HunterLab obj) => obj.GetHashCode(); - - /// - public bool Equals(Hsl x, Hsl y) - { - return this.Equals(x.H, y.H) - && this.Equals(x.S, y.S) - && this.Equals(x.L, y.L); - } - - /// - public int GetHashCode(Hsl obj) => obj.GetHashCode(); - - /// - public bool Equals(Hsv x, Hsv y) - { - return this.Equals(x.H, y.H) - && this.Equals(x.S, y.S) - && this.Equals(x.V, y.V); - } - - /// - public int GetHashCode(Hsv obj) => obj.GetHashCode(); - - /// - public bool Equals(Lms x, Lms y) - { - return this.Equals(x.L, y.L) - && this.Equals(x.M, y.M) - && this.Equals(x.S, y.S); - } - - /// - public int GetHashCode(Lms obj) => obj.GetHashCode(); - - /// - public bool Equals(YCbCr x, YCbCr y) - { - return this.Equals(x.Y, y.Y) - && this.Equals(x.Cb, y.Cb) - && this.Equals(x.Cr, y.Cr); - } - - /// - public int GetHashCode(YCbCr obj) => obj.GetHashCode(); - - /// - public bool Equals(CieXyChromaticityCoordinates x, CieXyChromaticityCoordinates y) => this.Equals(x.X, y.X) && this.Equals(x.Y, y.Y); - - /// - public int GetHashCode(CieXyChromaticityCoordinates obj) => obj.GetHashCode(); - - /// - public bool Equals(RgbPrimariesChromaticityCoordinates x, RgbPrimariesChromaticityCoordinates y) => this.Equals(x.R, y.R) && this.Equals(x.G, y.G) && this.Equals(x.B, y.B); - - /// - public int GetHashCode(RgbPrimariesChromaticityCoordinates obj) => obj.GetHashCode(); - - /// - public bool Equals(GammaWorkingSpace x, GammaWorkingSpace y) - { - if (x is GammaWorkingSpace g1 && y is GammaWorkingSpace g2) - { - return this.Equals(g1.Gamma, g2.Gamma) - && this.Equals(g1.WhitePoint, g2.WhitePoint) - && this.Equals(g1.ChromaticityCoordinates, g2.ChromaticityCoordinates); - } - - return false; - } - - /// - public int GetHashCode(GammaWorkingSpace obj) => obj.GetHashCode(); - - /// - public bool Equals(RgbWorkingSpace x, RgbWorkingSpace y) - { - return this.Equals(x.WhitePoint, y.WhitePoint) - && this.Equals(x.ChromaticityCoordinates, y.ChromaticityCoordinates); - } - - /// - public int GetHashCode(RgbWorkingSpace obj) => obj.GetHashCode(); - - private bool Equals(float x, float y) - { - float d = x - y; - return d >= -this.epsilon && d <= this.epsilon; - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.cs deleted file mode 100644 index d71f0fa5e..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchConversionTests.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieLabAndCieLchConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(54.2917, 106.8391, 40.8526, 54.2917, 80.8125, 69.8851)] - [InlineData(100, 0, 0, 100, 0, 0)] - [InlineData(100, 50, 180, 100, -50, 0)] - [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] - [InlineData(10, 36.0555, 123.6901, 10, -20, 30)] - [InlineData(10, 36.0555, 303.6901, 10, 20, -30)] - [InlineData(10, 36.0555, 236.3099, 10, -20, -30)] - public void Convert_Lch_to_Lab(float l, float c, float h, float l2, float a, float b) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new CieLab(l2, a, b); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(54.2917, 80.8125, 69.8851, 54.2917, 106.8391, 40.8526)] - [InlineData(100, 0, 0, 100, 0, 0)] - [InlineData(100, -50, 0, 100, 50, 180)] - [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] - [InlineData(10, -20, 30, 10, 36.0555, 123.6901)] - [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] - [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] - public void Convert_Lab_to_Lch(float l, float a, float b, float l2, float c, float h) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new CieLch(l2, c, h); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.cs deleted file mode 100644 index d8378217e..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLchuvConversionTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieLabAndCieLchuvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(30.66194, 200, 352.7564, 31.95653, 116.8745, 2.388602)] - public void Convert_Lchuv_to_Lab(float l, float c, float h, float l2, float a, float b) - { - // Arrange - var input = new CieLchuv(l, c, h); - var expected = new CieLab(l2, a, b); - - Span inputSpan = new CieLchuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 30.66194, 200, 352.7564)] - public void Convert_Lab_to_Lchuv(float l, float a, float b, float l2, float c, float h) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new CieLchuv(l2, c, h); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLchuv[5]; - - // Act - var actual = Converter.ToCieLchuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLuvConversionTests.cs deleted file mode 100644 index 220ef4f22..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieLuvConversionTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieLabAndCieLuvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(10, 36.0555, 303.6901, 10.0151367, -23.9644356, 17.0226)] - public void Convert_CieLuv_to_CieLab(float l, float u, float v, float l2, float a, float b) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new CieLab(l2, a, b); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(10.0151367, -23.9644356, 17.0226, 10.0000038, -12.830183, 15.1829338)] - public void Convert_CieLab_to_CieLuv(float l, float a, float b, float l2, float u, float v) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new CieLuv(l2, u, v); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.cs deleted file mode 100644 index 49ea4502f..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCieXyyConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndCieXyyConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.8644734, 0.06098868, 0.06509002, 36.05552, 275.6228, 10.01517)] - public void Convert_CieXyy_to_CieLab(float x, float y, float yl, float l, float a, float b) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new CieLab(l, a, b); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 0.8644734, 0.06098868, 0.06509002)] - public void Convert_CieLab_to_CieXyy(float l, float a, float b, float x, float y, float yl) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - var actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCmykConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCmykConversionTests.cs deleted file mode 100644 index 7f9470e20..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndCmykConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndCmykConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 1, 0, 0, 0)] - [InlineData(0, 1, 0.6156551, 5.960464E-08, 55.063, 82.54871, 23.16506)] - public void Convert_Cmyk_to_CieLab(float c, float m, float y, float k, float l, float a, float b) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new CieLab(l, a, b); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0, 1)] - [InlineData(36.0555, 303.6901, 10.01514, 0, 1, 0.6156551, 5.960464E-08)] - public void Convert_CieLab_to_Cmyk(float l, float a, float b, float c, float m, float y, float k) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHslConversionTests.cs deleted file mode 100644 index 1af6a9315..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHslConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndHslConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(336.9393, 1, 0.5, 55.063, 82.54868, 23.16508)] - public void Convert_Hsl_to_CieLab(float h, float s, float ll, float l, float a, float b) - { - // Arrange - var input = new Hsl(h, s, ll); - var expected = new CieLab(l, a, b); - - Span inputSpan = new Hsl[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 336.9393, 1, 0.5)] - public void Convert_CieLab_to_Hsl(float l, float a, float b, float h, float s, float ll) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new Hsl(h, s, ll); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsl[5]; - - // Act - var actual = Converter.ToHsl(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHsvConversionTests.cs deleted file mode 100644 index c7c3ad19b..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHsvConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndHsvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(336.9393, 1, 0.9999999, 55.063, 82.54871, 23.16504)] - public void Convert_Hsv_to_CieLab(float h, float s, float v, float l, float a, float b) - { - // Arrange - var input = new Hsv(h, s, v); - var expected = new CieLab(l, a, b); - - Span inputSpan = new Hsv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 336.9393, 1, 0.9999999)] - public void Convert_CieLab_to_Hsv(float l, float a, float b, float h, float s, float v) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new Hsv(h, s, v); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsv[5]; - - // Act - var actual = Converter.ToHsv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHunterLabConversionTests.cs deleted file mode 100644 index 81fff6e14..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndHunterLabConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndHunterLabConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(27.51646, 556.9392, -0.03974226, 36.05554, 275.6227, 10.01519)] - public void Convert_HunterLab_to_CieLab(float l2, float a2, float b2, float l, float a, float b) - { - // Arrange - var input = new HunterLab(l2, a2, b2); - var expected = new CieLab(l, a, b); - - Span inputSpan = new HunterLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 27.51646, 556.9392, -0.03974226)] - public void Convert_CieLab_to_HunterLab(float l, float a, float b, float l2, float a2, float b2) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new HunterLab(l2, a2, b2); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new HunterLab[5]; - - // Act - var actual = Converter.ToHunterLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLinearRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLinearRgbConversionTests.cs deleted file mode 100644 index 68547d8a2..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLinearRgbConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndLinearRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 0, 0.1221596, 55.063, 82.54871, 23.16505)] - public void Convert_LinearRgb_to_CieLab(float r, float g, float b2, float l, float a, float b) - { - // Arrange - var input = new LinearRgb(r, g, b2); - var expected = new CieLab(l, a, b); - - Span inputSpan = new LinearRgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 1, 0, 0.1221596)] - public void Convert_CieLab_to_LinearRgb(float l, float a, float b, float r, float g, float b2) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new LinearRgb(r, g, b2); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new LinearRgb[5]; - - // Act - var actual = Converter.ToLinearRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLmsConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLmsConversionTests.cs deleted file mode 100644 index 2918b6f62..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndLmsConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndLmsConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.8303261, -0.5776886, 0.1133359, 36.05553, 275.6228, 10.01518)] - public void Convert_Lms_to_CieLab(float l2, float m, float s, float l, float a, float b) - { - // Arrange - var input = new Lms(l2, m, s); - var expected = new CieLab(l, a, b); - - Span inputSpan = new Lms[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 0.8303261, -0.5776886, 0.1133359)] - public void Convert_CieLab_to_Lms(float l, float a, float b, float l2, float m, float s) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new Lms(l2, m, s); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new Lms[5]; - - // Act - var actual = Converter.ToLms(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndRgbConversionTests.cs deleted file mode 100644 index 3acf695d1..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndRgbConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.9999999, 0, 0.384345, 55.063, 82.54871, 23.16505)] - public void Convert_Rgb_to_CieLab(float r, float g, float b2, float l, float a, float b) - { - // Arrange - var input = new Rgb(r, g, b2); - var expected = new CieLab(l, a, b); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 303.6901, 10.01514, 0.9999999, 0, 0.384345)] - public void Convert_CieLab_to_Rgb(float l, float a, float b, float r, float g, float b2) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new Rgb(r, g, b2); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = Converter.ToRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndYCbCrConversionTests.cs deleted file mode 100644 index f13e989a8..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLabAndYCbCrConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLabAndYCbCrConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 128, 128, 0, 0, 0)] - [InlineData(87.4179, 133.9763, 247.5308, 55.06287, 82.54838, 23.1697)] - public void Convert_YCbCr_to_CieLab(float y, float cb, float cr, float l, float a, float b) - { - // Arrange - var input = new YCbCr(y, cb, cr); - var expected = new CieLab(l, a, b); - - Span inputSpan = new YCbCr[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = Converter.ToCieLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 128, 128)] - [InlineData(36.0555, 303.6901, 10.01514, 87.4179, 133.9763, 247.5308)] - public void Convert_CieLab_to_YCbCr(float l, float a, float b, float y, float cb, float cr) - { - // Arrange - var input = new CieLab(l, a, b); - var expected = new YCbCr(y, cb, cr); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new YCbCr[5]; - - // Act - var actual = Converter.ToYCbCr(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieLuvConversionTests.cs deleted file mode 100644 index b50d47aeb..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieLuvConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndCieLuvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 34.89777, 187.6642, -7.181467)] - public void Convert_CieLch_to_CieLuv(float l, float c, float h, float l2, float u, float v) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new CieLuv(l2, u, v); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(34.89777, 187.6642, -7.181467, 36.05552, 103.6901, 10.01514)] - public void Convert_CieLuv_to_CieLch(float l2, float u, float v, float l, float c, float h) - { - // Arrange - var input = new CieLuv(l2, u, v); - var expected = new CieLch(l, c, h); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieXyyConversionTests.cs deleted file mode 100644 index 93f082886..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndCieXyyConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndCieXyyConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 0.6529307, 0.2147411, 0.08447381)] - public void Convert_CieLch_to_CieXyy(float l, float c, float h, float x, float y, float yl) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - var actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.6529307, 0.2147411, 0.08447381, 36.05552, 103.6901, 10.01515)] - public void Convert_CieXyy_to_CieLch(float x, float y, float yl, float l, float c, float h) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new CieLch(l, c, h); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHslConversionTests.cs deleted file mode 100644 index 0bc76562b..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHslConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndHslConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 341.959, 1, 0.4207301)] - public void Convert_CieLch_to_Hsl(float l, float c, float h, float h2, float s, float l2) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new Hsl(h2, s, l2); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsl[5]; - - // Act - var actual = Converter.ToHsl(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(341.959, 1, 0.4207301, 46.13444, 78.0637, 22.90503)] - public void Convert_Hsl_to_CieLch(float h2, float s, float l2, float l, float c, float h) - { - // Arrange - var input = new Hsl(h2, s, l2); - var expected = new CieLch(l, c, h); - - Span inputSpan = new Hsl[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHsvConversionTests.cs deleted file mode 100644 index bb28c0394..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHsvConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndHsvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 341.959, 1, 0.8414602)] - public void Convert_CieLch_to_Hsv(float l, float c, float h, float h2, float s, float v) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new Hsv(h2, s, v); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsv[5]; - - // Act - var actual = Converter.ToHsv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(341.959, 1, 0.8414602, 46.13444, 78.0637, 22.90501)] - public void Convert_Hsv_to_CieLch(float h2, float s, float v, float l, float c, float h) - { - // Arrange - var input = new Hsv(h2, s, v); - var expected = new CieLch(l, c, h); - - Span inputSpan = new Hsv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHunterLabConversionTests.cs deleted file mode 100644 index 9892ac8f5..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndHunterLabConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndHunterLabConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 29.41358, 106.6302, 9.102425)] - public void Convert_CieLch_to_HunterLab(float l, float c, float h, float l2, float a, float b) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new HunterLab(l2, a, b); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new HunterLab[5]; - - // Act - var actual = Converter.ToHunterLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(29.41358, 106.6302, 9.102425, 36.05551, 103.6901, 10.01515)] - public void Convert_HunterLab_to_CieLch(float l2, float a, float b, float l, float c, float h) - { - // Arrange - var input = new HunterLab(l2, a, b); - var expected = new CieLch(l, c, h); - - Span inputSpan = new HunterLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLinearRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLinearRgbConversionTests.cs deleted file mode 100644 index 3ecfadf11..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLinearRgbConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndLinearRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 0.6765013, 0, 0.05209038)] - public void Convert_CieLch_to_LinearRgb(float l, float c, float h, float r, float g, float b) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new LinearRgb(r, g, b); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new LinearRgb[5]; - - // Act - var actual = Converter.ToLinearRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.6765013, 0, 0.05209038, 46.13445, 78.06367, 22.90504)] - public void Convert_LinearRgb_to_CieLch(float r, float g, float b, float l, float c, float h) - { - // Arrange - var input = new LinearRgb(r, g, b); - var expected = new CieLch(l, c, h); - - Span inputSpan = new LinearRgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLmsConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLmsConversionTests.cs deleted file mode 100644 index 7a1939121..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndLmsConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndLmsConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 0.2440057, -0.04603009, 0.05780027)] - public void Convert_CieLch_to_Lms(float l, float c, float h, float l2, float m, float s) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new Lms(l2, m, s); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new Lms[5]; - - // Act - var actual = Converter.ToLms(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.2440057, -0.04603009, 0.05780027, 36.05552, 103.6901, 10.01515)] - public void Convert_Lms_to_CieLch(float l2, float m, float s, float l, float c, float h) - { - // Arrange - var input = new Lms(l2, m, s); - var expected = new CieLch(l, c, h); - - Span inputSpan = new Lms[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndRgbConversionTests.cs deleted file mode 100644 index 013e79534..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndRgbConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 0.8414602, 0, 0.2530123)] - public void Convert_CieLch_to_Rgb(float l, float c, float h, float r, float g, float b) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new Rgb(r, g, b); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = Converter.ToRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.8414602, 0, 0.2530123, 46.13444, 78.0637, 22.90503)] - public void Convert_Rgb_to_CieLch(float r, float g, float b, float l, float c, float h) - { - // Arrange - var input = new Rgb(r, g, b); - var expected = new CieLch(l, c, h); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndYCbCrConversionTests.cs deleted file mode 100644 index bcde97102..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchAndYCbCrConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchAndYCbCrConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 128, 128)] - [InlineData(36.0555, 103.6901, 10.01514, 71.5122, 124.053, 230.0401)] - public void Convert_CieLch_to_YCbCr(float l, float c, float h, float y, float cb, float cr) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new YCbCr(y, cb, cr); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new YCbCr[5]; - - // Act - var actual = Converter.ToYCbCr(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(71.5122, 124.053, 230.0401, 46.23178, 78.1114, 22.7662)] - public void Convert_YCbCr_to_CieLch(float y, float cb, float cr, float l, float c, float h) - { - // Arrange - var input = new YCbCr(y, cb, cr); - var expected = new CieLch(l, c, h); - - Span inputSpan = new YCbCr[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLchConversionTests.cs deleted file mode 100644 index 1098de91e..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLchConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchuvAndCieLchConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.73742, 64.79149, 30.1786, 36.0555, 103.6901, 10.01513)] - public void Convert_CieLch_to_CieLchuv(float l2, float c2, float h2, float l, float c, float h) - { - // Arrange - var input = new CieLch(l2, c2, h2); - var expected = new CieLchuv(l, c, h); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLchuv[5]; - - // Act - var actual = Converter.ToCieLchuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(36.0555, 103.6901, 10.01514, 36.73742, 64.79149, 30.1786)] - public void Convert_CieLchuv_to_CieLch(float l, float c, float h, float l2, float c2, float h2) - { - // Arrange - var input = new CieLchuv(l, c, h); - var expected = new CieLch(l2, c2, h2); - - Span inputSpan = new CieLchuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLuvConversionTests.cs deleted file mode 100644 index ba3bc9e79..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCieLuvConversionTests.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieLchuvAndCieLuvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(54.2917, 106.8391, 40.8526, 54.2917, 80.8125, 69.8851)] - [InlineData(100, 0, 0, 100, 0, 0)] - [InlineData(100, 50, 180, 100, -50, 0)] - [InlineData(10, 36.0555, 56.3099, 10, 20, 30)] - [InlineData(10, 36.0555, 123.6901, 10, -20, 30)] - [InlineData(10, 36.0555, 303.6901, 10, 20, -30)] - [InlineData(10, 36.0555, 236.3099, 10, -20, -30)] - public void Convert_CieLchuv_to_CieLuv(float l, float c, float h, float l2, float u, float v) - { - // Arrange - var input = new CieLchuv(l, c, h); - var expected = new CieLuv(l2, u, v); - - Span inputSpan = new CieLchuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(54.2917, 80.8125, 69.8851, 54.2917, 106.8391, 40.8526)] - [InlineData(100, 0, 0, 100, 0, 0)] - [InlineData(100, -50, 0, 100, 50, 180)] - [InlineData(10, 20, 30, 10, 36.0555, 56.3099)] - [InlineData(10, -20, 30, 10, 36.0555, 123.6901)] - [InlineData(10, 20, -30, 10, 36.0555, 303.6901)] - [InlineData(10, -20, -30, 10, 36.0555, 236.3099)] - [InlineData(37.3511, 24.1720, 16.0684, 37.3511, 29.0255, 33.6141)] - public void Convert_CieLuv_to_CieLchuv(float l, float u, float v, float l2, float c, float h) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new CieLchuv(l2, c, h); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLchuv[5]; - - // Act - var actual = Converter.ToCieLchuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCmykConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCmykConversionTests.cs deleted file mode 100644 index 176a0e217..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLchuvAndCmykConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLchuvAndCmykConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 1, 0, 0, 0)] - [InlineData(0, 0.8576171, 0.7693201, 0.3440427, 36.0555, 103.6901, 10.01514)] - public void Convert_Cmyk_to_CieLchuv(float c2, float m, float y, float k, float l, float c, float h) - { - // Arrange - var input = new Cmyk(c2, m, y, k); - var expected = new CieLchuv(l, c, h); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLchuv[5]; - - // Act - var actual = Converter.ToCieLchuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0, 1)] - [InlineData(36.0555, 103.6901, 10.01514, 0, 0.8576171, 0.7693201, 0.3440427)] - public void Convert_CieLchuv_to_Cmyk(float l, float c, float h, float c2, float m, float y, float k) - { - // Arrange - var input = new CieLchuv(l, c, h); - var expected = new Cmyk(c2, m, y, k); - - Span inputSpan = new CieLchuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndCieXyyConversionTests.cs deleted file mode 100644 index e32cc6ebf..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndCieXyyConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndCieXyyConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 103.6901, 10.01514, 0.5646762, 0.2932749, 0.09037033)] - public void Convert_CieLuv_to_CieXyy(float l, float u, float v, float x, float y, float yl) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - var actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.5646762, 0.2932749, 0.09037033, 36.0555, 103.6901, 10.01514)] - public void Convert_CieXyy_to_CieLuv(float x, float y, float yl, float l, float u, float v) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHslConversionTests.cs deleted file mode 100644 index 95f07465a..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHslConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndHslConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 93.6901, 10.01514, 347.3767, 0.7115612, 0.3765343)] - public void Convert_CieLuv_to_Hsl(float l, float u, float v, float h, float s, float l2) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new Hsl(h, s, l2); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsl[5]; - - // Act - var actual = Converter.ToHsl(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(347.3767, 0.7115612, 0.3765343, 36.0555, 93.69012, 10.01514)] - public void Convert_Hsl_to_CieLuv(float h, float s, float l2, float l, float u, float v) - { - // Arrange - var input = new Hsl(h, s, l2); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new Hsl[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHsvConversionTests.cs deleted file mode 100644 index ddb90f0ef..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHsvConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndHsvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 93.6901, 10.01514, 347.3767, 0.8314762, 0.6444615)] - public void Convert_CieLuv_to_Hsv(float l, float u, float v, float h, float s, float v2) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new Hsv(h, s, v2); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsv[5]; - - // Act - var actual = Converter.ToHsv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(347.3767, 0.8314762, 0.6444615, 36.0555, 93.69012, 10.01514)] - public void Convert_Hsv_to_CieLuv(float h, float s, float v2, float l, float u, float v) - { - // Arrange - var input = new Hsv(h, s, v2); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new Hsv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHunterLabConversionTests.cs deleted file mode 100644 index 38449e4b7..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndHunterLabConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndHunterLabConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 93.6901, 10.01514, 30.19531, 46.4312, 11.16259)] - public void Convert_CieLuv_to_HunterLab(float l, float u, float v, float l2, float a, float b) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new HunterLab(l2, a, b); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new HunterLab[5]; - - // Act - var actual = Converter.ToHunterLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(30.19531, 46.4312, 11.16259, 36.0555, 93.6901, 10.01514)] - public void Convert_HunterLab_to_CieLuv(float l2, float a, float b, float l, float u, float v) - { - // Arrange - var input = new HunterLab(l2, a, b); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new HunterLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLinearRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLinearRgbConversionTests.cs deleted file mode 100644 index f3bd93604..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLinearRgbConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndLinearRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 93.6901, 10.01514, 0.3729299, 0.01141088, 0.04014909)] - public void Convert_CieLuv_to_LinearRgb(float l, float u, float v, float r, float g, float b) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new LinearRgb(r, g, b); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new LinearRgb[5]; - - // Act - var actual = Converter.ToLinearRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.3729299, 0.01141088, 0.04014909, 36.0555, 93.6901, 10.01511)] - public void Convert_LinearRgb_to_CieLuv(float r, float g, float b, float l, float u, float v) - { - // Arrange - var input = new LinearRgb(r, g, b); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new LinearRgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLmsConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLmsConversionTests.cs deleted file mode 100644 index ac90a7ba4..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndLmsConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndLmsConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 93.6901, 10.01514, 0.164352, 0.03267485, 0.0483408)] - public void Convert_CieLuv_to_Lms(float l, float u, float v, float l2, float m, float s) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new Lms(l2, m, s); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Lms[5]; - - // Act - var actual = Converter.ToLms(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.164352, 0.03267485, 0.0483408, 36.0555, 93.69009, 10.01514)] - public void Convert_Lms_to_CieLuv(float l2, float m, float s, float l, float u, float v) - { - // Arrange - var input = new Lms(l2, m, s); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new Lms[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndRgbConversionTests.cs deleted file mode 100644 index b2e308fce..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndRgbConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(36.0555, 93.6901, 10.01514, 0.6444615, 0.1086071, 0.2213444)] - public void Convert_CieLuv_to_Rgb(float l, float u, float v, float r, float g, float b) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new Rgb(r, g, b); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = Converter.ToRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.6444615, 0.1086071, 0.2213444, 36.0555, 93.69012, 10.01514)] - public void Convert_Rgb_to_CieLuv(float r, float g, float b, float l, float u, float v) - { - // Arrange - var input = new Rgb(r, g, b); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndYCbCrConversionTests.cs deleted file mode 100644 index f7c6372b1..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieLuvAndYCbCrConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieLuvAndYCbCrConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 128, 128)] - [InlineData(36.0555, 93.6901, 10.01514, 71.8283, 119.3174, 193.9839)] - public void Convert_CieLuv_to_YCbCr(float l, float u, float v, float y, float cb, float cr) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new YCbCr(y, cb, cr); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new YCbCr[5]; - - // Act - var actual = Converter.ToYCbCr(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 128, 128, 0, 0, 0)] - [InlineData(71.8283, 119.3174, 193.9839, 36.00565, 93.44593, 10.2234)] - public void Convert_YCbCr_to_CieLuv(float y, float cb, float cr, float l, float u, float v) - { - // Arrange - var input = new YCbCr(y, cb, cr); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new YCbCr[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHslConversionTests.cs deleted file mode 100644 index bae914002..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHslConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyyAndHslConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.211263)] - public void Convert_CieXyy_to_Hsl(float x, float y, float yl, float h, float s, float l) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new Hsl(h, s, l); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsl[5]; - - // Act - var actual = Converter.ToHsl(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(120, 1, 0.211263, 0.3, 0.6, 0.1067051)] - public void Convert_Hsl_to_CieXyy(float h, float s, float l, float x, float y, float yl) - { - // Arrange - var input = new Hsl(h, s, l); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new Hsl[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - CieXyy actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHsvConversionTests.cs deleted file mode 100644 index 7de91cc32..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHsvConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyyAndHsvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.4225259)] - public void Convert_CieXyy_to_Hsv(float x, float y, float yl, float h, float s, float v) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new Hsv(h, s, v); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsv[5]; - - // Act - var actual = Converter.ToHsv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(120, 1, 0.4225259, 0.3, 0.6, 0.1067051)] - public void Convert_Hsv_to_CieXyy(float h, float s, float v, float x, float y, float yl) - { - // Arrange - var input = new Hsv(h, s, v); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new Hsv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - var actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHunterLabConversionTests.cs deleted file mode 100644 index 40b58b704..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndHunterLabConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyyAndHunterLabConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 31.46263, -32.81796, 28.64938)] - public void Convert_CieXyy_to_HunterLab(float x, float y, float yl, float l, float a, float b) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new HunterLab(l, a, b); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new HunterLab[5]; - - // Act - var actual = Converter.ToHunterLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(31.46263, -32.81796, 28.64938, 0.3605552, 0.9369011, 0.1001514)] - public void Convert_HunterLab_to_CieXyy(float l, float a, float b, float x, float y, float yl) - { - // Arrange - var input = new HunterLab(l, a, b); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new HunterLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - CieXyy actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLinearRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLinearRgbConversionTests.cs deleted file mode 100644 index 062f54abc..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLinearRgbConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyyAndLinearRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 0, 0.1492062, 0)] - public void Convert_CieXyy_to_LinearRgb(float x, float y, float yl, float r, float g, float b) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new LinearRgb(r, g, b); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new LinearRgb[5]; - - // Act - var actual = Converter.ToLinearRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0, 0.1492062, 0, 0.3, 0.6, 0.1067051)] - public void Convert_LinearRgb_to_CieXyy(float r, float g, float b, float x, float y, float yl) - { - // Arrange - var input = new LinearRgb(r, g, b); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new LinearRgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - var actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLmsConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLmsConversionTests.cs deleted file mode 100644 index 8e9cbc5ff..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndLmsConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyyAndLmsConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 0.06631134, 0.1415282, -0.03809926)] - public void Convert_CieXyy_to_Lms(float x, float y, float yl, float l, float m, float s) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new Lms(l, m, s); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new Lms[5]; - - // Act - var actual = Converter.ToLms(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.06631134, 0.1415282, -0.03809926, 0.360555, 0.9369009, 0.1001514)] - public void Convert_Lms_to_CieXyy(float l, float m, float s, float x, float y, float yl) - { - // Arrange - var input = new Lms(l, m, s); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new Lms[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - CieXyy actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndRgbConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndRgbConversionTests.cs deleted file mode 100644 index 0a7cd6842..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndRgbConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyyAndRgbConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 0, 0.4225259, 0)] - public void Convert_CieXyy_to_Rgb(float x, float y, float yl, float r, float g, float b) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new Rgb(r, g, b); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = Converter.ToRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0, 0.4225259, 0, 0.3, 0.6, 0.1067051)] - public void Convert_Rgb_to_CieXyy(float r, float g, float b, float x, float y, float yl) - { - // Arrange - var input = new Rgb(r, g, b); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - CieXyy actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndYCbCrConversionTests.cs deleted file mode 100644 index efacebcf1..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyyAndYCbCrConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyyAndYCbCrConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 128, 128)] - [InlineData(0.360555, 0.936901, 0.1001514, 63.24579, 92.30826, 82.88884)] - public void Convert_CieXyy_to_YCbCr(float x, float y, float yl, float y2, float cb, float cr) - { - // Arrange - var input = new CieXyy(x, y, yl); - var expected = new YCbCr(y2, cb, cr); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new YCbCr[5]; - - // Act - var actual = Converter.ToYCbCr(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 128, 128, 0, 0, 0)] - [InlineData(63.24579, 92.30826, 82.88884, 0.3, 0.6, 0.1072441)] - public void Convert_YCbCr_to_CieXyy(float y2, float cb, float cr, float x, float y, float yl) - { - // Arrange - var input = new YCbCr(y2, cb, cr); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new YCbCr[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - CieXyy actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLabConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLabConversionTest.cs deleted file mode 100644 index 3ea4228e5..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLabConversionTest.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieXyzAndCieLabConversionTest -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from to (). - /// - [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 - var input = new CieLab(l, a, b, Illuminants.D65); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; - var converter = new ColorSpaceConverter(options); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new CieLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = converter.ToCieXyz(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from () to . - /// - [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 - var input = new CieXyz(x, y, z); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; - var converter = new ColorSpaceConverter(options); - var expected = new CieLab(l, a, b); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLab[5]; - - // Act - var actual = converter.ToCieLab(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchConversionTests.cs deleted file mode 100644 index 698c9add6..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyzAndCieLchConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.360555, 0.936901, 0.1001514, 97.50815, 155.8035, 139.323)] - public void Convert_CieXyz_to_CieLch(float x, float y, float yl, float l, float c, float h) - { - // Arrange - var input = new CieXyz(x, y, yl); - var expected = new CieLch(l, c, h); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(97.50815, 155.8035, 139.323, 0.3605551, 0.936901, 0.1001514)] - public void Convert_CieLch_to_CieXyz(float l, float c, float h, float x, float y, float yl) - { - // Arrange - var input = new CieLch(l, c, h); - var expected = new CieXyz(x, y, yl); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - CieXyz actual = Converter.ToCieXyz(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchuvConversionTests.cs deleted file mode 100644 index a4caf4c85..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLchuvConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyzAndCieLchuvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.360555, 0.936901, 0.1001514, 97.50697, 183.3831, 133.6321)] - public void Convert_CieXyz_to_CieLchuv(float x, float y, float yl, float l, float c, float h) - { - // Arrange - var input = new CieXyz(x, y, yl); - var expected = new CieLchuv(l, c, h); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLchuv[5]; - - // Act - var actual = Converter.ToCieLchuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(97.50697, 183.3831, 133.6321, 0.360555, 0.936901, 0.1001515)] - public void Convert_CieLchuv_to_CieXyz(float l, float c, float h, float x, float y, float yl) - { - // Arrange - var input = new CieLchuv(l, c, h); - var expected = new CieXyz(x, y, yl); - - Span inputSpan = new CieLchuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - CieXyz actual = Converter.ToCieXyz(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLuvConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLuvConversionTest.cs deleted file mode 100644 index 9e3381b40..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieLuvConversionTest.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieXyzAndCieLuvConversionTest -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from to (). - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0, 100, 50, 0, 0, 0)] - [InlineData(0.1, 100, 50, 0.000493, 0.000111, 0)] - [InlineData(70.0000, 86.3525, 2.8240, 0.569310, 0.407494, 0.365843)] - [InlineData(10.0000, -1.2345, -10.0000, 0.012191, 0.011260, 0.025939)] - [InlineData(100, 0, 0, 0.950470, 1.000000, 1.088830)] - [InlineData(1, 1, 1, 0.001255, 0.001107, 0.000137)] - public void Convert_Luv_to_Xyz(float l, float u, float v, float x, float y, float z) - { - // Arrange - var input = new CieLuv(l, u, v, Illuminants.D65); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; - var converter = new ColorSpaceConverter(options); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = converter.ToCieXyz(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from () to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.000493, 0.000111, 0, 0.1003, 0.9332, -0.0070)] - [InlineData(0.569310, 0.407494, 0.365843, 70.0000, 86.3524, 2.8240)] - [InlineData(0.012191, 0.011260, 0.025939, 9.9998, -1.2343, -9.9999)] - [InlineData(0.950470, 1.000000, 1.088830, 100, 0, 0)] - [InlineData(0.001255, 0.001107, 0.000137, 0.9999, 0.9998, 1.0004)] - public void Convert_Xyz_to_Luv(float x, float y, float z, float l, float u, float v) - { - // Arrange - var input = new CieXyz(x, y, z); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetLabWhitePoint = Illuminants.D65 }; - var converter = new ColorSpaceConverter(options); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = converter.ToCieLuv(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieXyyConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieXyyConversionTest.cs deleted file mode 100644 index 5ef5c4d21..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndCieXyyConversionTest.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieXyzAndCieXyyConversionTest -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - [Theory] - [InlineData(0.436075, 0.222504, 0.013932, 0.648427, 0.330856, 0.222504)] - [InlineData(0.964220, 1.000000, 0.825210, 0.345669, 0.358496, 1.000000)] - [InlineData(0.434119, 0.356820, 0.369447, 0.374116, 0.307501, 0.356820)] - [InlineData(0, 0, 0, 0.538842, 0.000000, 0.000000)] - public void Convert_xyY_to_XYZ(float xyzX, float xyzY, float xyzZ, float x, float y, float yl) - { - var input = new CieXyy(x, y, yl); - var expected = new CieXyz(xyzX, xyzY, xyzZ); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = ColorSpaceConverter.ToCieXyz(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - [Theory] - [InlineData(0.436075, 0.222504, 0.013932, 0.648427, 0.330856, 0.222504)] - [InlineData(0.964220, 1.000000, 0.825210, 0.345669, 0.358496, 1.000000)] - [InlineData(0.434119, 0.356820, 0.369447, 0.374116, 0.307501, 0.356820)] - [InlineData(0.231809, 0, 0.077528, 0.749374, 0.000000, 0.000000)] - public void Convert_XYZ_to_xyY(float xyzX, float xyzY, float xyzZ, float x, float y, float yl) - { - var input = new CieXyz(xyzX, xyzY, xyzZ); - var expected = new CieXyy(x, y, yl); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - var actual = ColorSpaceConverter.ToCieXyy(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHslConversionTests.cs deleted file mode 100644 index 634d03a50..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHslConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyzAndHslConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.5)] - public void Convert_CieXyz_to_Hsl(float x, float y, float yl, float h, float s, float l) - { - // Arrange - var input = new CieXyz(x, y, yl); - var expected = new Hsl(h, s, l); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsl[5]; - - // Act - var actual = Converter.ToHsl(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(120, 1, 0.5, 0.3575761, 0.7151522, 0.119192)] - public void Convert_Hsl_to_CieXyz(float h, float s, float l, float x, float y, float yl) - { - // Arrange - var input = new Hsl(h, s, l); - var expected = new CieXyz(x, y, yl); - - Span inputSpan = new Hsl[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = Converter.ToCieXyz(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHsvConversionTests.cs deleted file mode 100644 index ccedd7b75..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHsvConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyzAndHsvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.360555, 0.936901, 0.1001514, 120, 1, 0.9999999)] - public void Convert_CieXyz_to_Hsv(float x, float y, float yl, float h, float s, float v) - { - // Arrange - var input = new CieXyz(x, y, yl); - var expected = new Hsv(h, s, v); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsv[5]; - - // Act - var actual = Converter.ToHsv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(120, 1, 0.9999999, 0.3575761, 0.7151522, 0.119192)] - public void Convert_Hsv_to_CieXyz(float h, float s, float v, float x, float y, float yl) - { - // Arrange - var input = new Hsv(h, s, v); - var expected = new CieXyz(x, y, yl); - - Span inputSpan = new Hsv[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - CieXyz actual = Converter.ToCieXyz(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHunterLabConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHunterLabConversionTest.cs deleted file mode 100644 index af7087ba2..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndHunterLabConversionTest.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class CieXyzAndHunterLabConversionTest -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from to (). - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(100, 0, 0, 0.98074, 1, 1.18232)] // C white point is HunterLab 100, 0, 0 - public void Convert_HunterLab_to_Xyz(float l, float a, float b, float x, float y, float z) - { - // Arrange - var input = new HunterLab(l, a, b); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.C }; - var converter = new ColorSpaceConverter(options); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new HunterLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = converter.ToCieXyz(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to (). - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(100, 0, 0, 0.95047, 1, 1.08883)] // D65 white point is HunerLab 100, 0, 0 (adaptation to C performed) - public void Convert_HunterLab_to_Xyz_D65(float l, float a, float b, float x, float y, float z) - { - // Arrange - var input = new HunterLab(l, a, b); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65 }; - var converter = new ColorSpaceConverter(options); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new HunterLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = converter.ToCieXyz(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from () to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.95047, 1, 1.08883, 100, 0, 0)] // D65 white point is HunterLab 100, 0, 0 (adaptation to C performed) - public void Convert_Xyz_D65_to_HunterLab(float x, float y, float z, float l, float a, float b) - { - // Arrange - var input = new CieXyz(x, y, z); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65 }; - var converter = new ColorSpaceConverter(options); - var expected = new HunterLab(l, a, b); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new HunterLab[5]; - - // Act - var actual = converter.ToHunterLab(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndLmsConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndLmsConversionTest.cs deleted file mode 100644 index 33bdc6e93..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndLmsConversionTest.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using original colorful library. -/// -public class CieXyzAndLmsConversionTest -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from () to . - /// - [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 - var input = new Lms(l, m, s); - var converter = new ColorSpaceConverter(); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new Lms[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = converter.ToCieXyz(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from () to . - /// - [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 - var input = new CieXyz(x, y, z); - var converter = new ColorSpaceConverter(); - var expected = new Lms(l, m, s); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new Lms[5]; - - // Act - var actual = converter.ToLms(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndYCbCrConversionTests.cs deleted file mode 100644 index ba67e605a..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CieXyzAndYCbCrConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CieXyzAndYCbCrConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 128, 128)] - [InlineData(0.360555, 0.936901, 0.1001514, 149.685, 43.52769, 21.23457)] - public void Convert_CieXyz_to_YCbCr(float x, float y, float z, float y2, float cb, float cr) - { - // Arrange - var input = new CieXyz(x, y, z); - var expected = new YCbCr(y2, cb, cr); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new YCbCr[5]; - - // Act - var actual = Converter.ToYCbCr(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 128, 128, 0, 0, 0)] - [InlineData(149.685, 43.52769, 21.23457, 0.3575761, 0.7151522, 0.119192)] - public void Convert_YCbCr_to_CieXyz(float y2, float cb, float cr, float x, float y, float z) - { - // Arrange - var input = new YCbCr(y2, cb, cr); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new YCbCr[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - CieXyz actual = Converter.ToCieXyz(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLchConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLchConversionTests.cs deleted file mode 100644 index 9def3e1b2..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLchConversionTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndCieLchConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 62.85025, 64.77041, 118.2425)] - public void Convert_Cmyk_to_CieLch(float c, float m, float y, float k, float l, float c2, float h) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new CieLch(l, c2, h); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLch[5]; - - // Act - var actual = Converter.ToCieLch(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(100, 3.81656E-05, 218.6598, 0, 1.192093E-07, 0, 5.960464E-08)] - [InlineData(62.85025, 64.77041, 118.2425, 0.286581, 0, 0.7975187, 0.34983)] - public void Convert_CieLch_to_Cmyk(float l, float c2, float h, float c, float m, float y, float k) - { - // Arrange - var input = new CieLch(l, c2, h); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new CieLch[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLuvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLuvConversionTests.cs deleted file mode 100644 index 9ae0fb711..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieLuvConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndCieLuvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 100, -1.937151E-05, 0)] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 62.66017, -24.01712, 68.29556)] - public void Convert_Cmyk_to_CieLuv(float c, float m, float y, float k, float l, float u, float v) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new CieLuv(l, u, v); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieLuv[5]; - - // Act - var actual = Converter.ToCieLuv(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(100, -1.937151E-05, 0, 3.576279E-07, 0, 0, 5.960464E-08)] - [InlineData(62.66017, -24.01712, 68.29556, 0.2865804, 0, 0.7975189, 0.3498302)] - public void Convert_CieLuv_to_Cmyk(float l, float u, float v, float c, float m, float y, float k) - { - // Arrange - var input = new CieLuv(l, u, v); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new CieLuv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyyConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyyConversionTests.cs deleted file mode 100644 index 270db9d20..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyyConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndCieXyyConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0.3127266, 0.3290231, 1)] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 0.3628971, 0.5289949, 0.3118104)] - public void Convert_Cmyk_to_CieXyy(float c, float m, float y, float k, float x, float y2, float yl) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new CieXyy(x, y2, yl); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyy[5]; - - // Act - var actual = Converter.ToCieXyy(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.3127266, 0.3290231, 1, 0, 0, 0, 5.960464E-08)] - [InlineData(0.3628971, 0.5289949, 0.3118104, 0.2865805, 0, 0.7975187, 0.3498302)] - public void Convert_CieXyy_to_Cmyk(float x, float y2, float yl, float c, float m, float y, float k) - { - // Arrange - var input = new CieXyy(x, y2, yl); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new CieXyy[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyzConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyzConversionTests.cs deleted file mode 100644 index 972948c71..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndCieXyzConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndCieXyzConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0.9504699, 1, 1.08883)] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 0.2139058, 0.3118104, 0.0637231)] - public void Convert_Cmyk_to_CieXyz(float c, float m, float y, float k, float x, float y2, float z) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new CieXyz(x, y2, z); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = Converter.ToCieXyz(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0.9504699, 1, 1.08883, 1.192093E-07, 0, 0, 5.960464E-08)] - [InlineData(0.2139058, 0.3118104, 0.0637231, 0.2865805, 0, 0.7975187, 0.3498302)] - public void Convert_CieXyz_to_Cmyk(float x, float y2, float z, float c, float m, float y, float k) - { - // Arrange - var input = new CieXyz(x, y2, z); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHslConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHslConversionTests.cs deleted file mode 100644 index 2b00942ac..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHslConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndHslConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0, 1)] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 81.56041, 0.6632275, 0.3909085)] - public void Convert_Cmyk_to_Hsl(float c, float m, float y, float k, float h, float s, float l) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new Hsl(h, s, l); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsl[5]; - - // Act - var actual = ColorSpaceConverter.ToHsl(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 1, 0, 0, 0, 0)] - [InlineData(81.56041, 0.6632275, 0.3909085, 0.2865805, 0, 0.7975187, 0.3498302)] - public void Convert_Hsl_to_Cmyk(float h, float s, float l, float c, float m, float y, float k) - { - // Arrange - var input = new Hsl(h, s, l); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new Hsl[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = ColorSpaceConverter.ToCmyk(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHsvConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHsvConversionTests.cs deleted file mode 100644 index f158fb5f9..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHsvConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndHsvConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0, 1)] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 81.56041, 0.7975187, 0.6501698)] - public void Convert_Cmyk_to_Hsv(float c, float m, float y, float k, float h, float s, float v) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new Hsv(h, s, v); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsv[5]; - - // Act - var actual = ColorSpaceConverter.ToHsv(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 1, 0, 0, 0, 0)] - [InlineData(81.56041, 0.7975187, 0.6501698, 0.2865805, 0, 0.7975187, 0.3498302)] - public void Convert_Hsv_to_Cmyk(float h, float s, float v, float c, float m, float y, float k) - { - // Arrange - var input = new Hsv(h, s, v); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new Hsv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = ColorSpaceConverter.ToCmyk(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHunterLabConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHunterLabConversionTests.cs deleted file mode 100644 index 9832a0d71..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndHunterLabConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndHunterLabConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 99.99999, 0, -1.66893E-05)] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 55.66742, -27.21679, 31.73834)] - public void Convert_Cmyk_to_HunterLab(float c, float m, float y, float k, float l, float a, float b) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new HunterLab(l, a, b); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new HunterLab[5]; - - // Act - var actual = Converter.ToHunterLab(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(99.99999, 0, -1.66893E-05, 1.192093E-07, 1.192093E-07, 0, 5.960464E-08)] - [InlineData(55.66742, -27.21679, 31.73834, 0.2865806, 0, 0.7975186, 0.3498301)] - public void Convert_HunterLab_to_Cmyk(float l, float a, float b, float c, float m, float y, float k) - { - // Arrange - var input = new HunterLab(l, a, b); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new HunterLab[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndYCbCrConversionTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndYCbCrConversionTests.cs deleted file mode 100644 index 1e8bc52e2..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/CmykAndYCbCrConversionTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -public class CmykAndYCbCrConversionTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0002F); - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 255, 128, 128)] - [InlineData(0.360555, 0.1036901, 0.818514, 0.274615, 136.5134, 69.90555, 114.9948)] - public void Convert_Cmyk_to_YCbCr(float c, float m, float y, float k, float y2, float cb, float cr) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new YCbCr(y2, cb, cr); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new YCbCr[5]; - - // Act - var actual = ColorSpaceConverter.ToYCbCr(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(255, 128, 128, 0, 0, 0, 5.960464E-08)] - [InlineData(136.5134, 69.90555, 114.9948, 0.2891567, 0, 0.7951807, 0.3490196)] - public void Convert_YCbCr_to_Cmyk(float y2, float cb, float cr, float c, float m, float y, float k) - { - // Arrange - var input = new YCbCr(y2, cb, cr); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new YCbCr[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = Converter.ToCmyk(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs deleted file mode 100644 index bbebf96b3..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/ColorConverterAdaptTest.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests methods. -/// Test data generated using: -/// -/// -/// -public class ColorConverterAdaptTest -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 1, 1, 1, 1, 1)] - [InlineData(0.206162, 0.260277, 0.746717, 0.220000, 0.130000, 0.780000)] - public void Adapt_RGB_WideGamutRGB_To_sRGB(float r1, float g1, float b1, float r2, float g2, float b2) - { - // Arrange - var input = new Rgb(r1, g1, b1, RgbWorkingSpaces.WideGamutRgb); - var expected = new Rgb(r2, g2, b2, RgbWorkingSpaces.SRgb); - var options = new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; - var converter = new ColorSpaceConverter(options); - - // Action - Rgb actual = converter.Adapt(input); - - // Assert - Assert.Equal(expected.WorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 1, 1, 1, 1, 1)] - [InlineData(0.220000, 0.130000, 0.780000, 0.206162, 0.260277, 0.746717)] - public void Adapt_RGB_SRGB_To_WideGamutRGB(float r1, float g1, float b1, float r2, float g2, float b2) - { - // Arrange - var input = new Rgb(r1, g1, b1, RgbWorkingSpaces.SRgb); - var expected = new Rgb(r2, g2, b2, RgbWorkingSpaces.WideGamutRgb); - var options = new ColorSpaceConverterOptions { TargetRgbWorkingSpace = RgbWorkingSpaces.WideGamutRgb }; - var converter = new ColorSpaceConverter(options); - - // Action - Rgb actual = converter.Adapt(input); - - // Assert - Assert.Equal(expected.WorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(22, 33, 1, 22.269869, 32.841164, 1.633926)] - public void Adapt_Lab_D65_To_D50(float l1, float a1, float b1, float l2, float a2, float b2) - { - // Arrange - var input = new CieLab(l1, a1, b1, Illuminants.D65); - var expected = new CieLab(l2, a2, b2); - var options = new ColorSpaceConverterOptions { TargetLabWhitePoint = Illuminants.D50 }; - var converter = new ColorSpaceConverter(options); - - // Action - CieLab actual = converter.Adapt(input); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.5, 0.5, 0.5, 0.510286, 0.501489, 0.378970)] - public void Adapt_Xyz_D65_To_D50_Bradford(float x1, float y1, float z1, float x2, float y2, float z2) - { - // Arrange - var input = new CieXyz(x1, y1, z1); - var expected = new CieXyz(x2, y2, z2); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D50 }; - var converter = new ColorSpaceConverter(options); - - // Action - CieXyz actual = converter.Adapt(input, Illuminants.D65); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.5, 0.5, 0.5, 0.507233, 0.500000, 0.378943)] - public void Adapt_Xyz_D65_To_D50_XyzScaling(float x1, float y1, float z1, float x2, float y2, float z2) - { - // Arrange - var input = new CieXyz(x1, y1, z1); - var expected = new CieXyz(x2, y2, z2); - var options = new ColorSpaceConverterOptions - { - ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling), - WhitePoint = Illuminants.D50 - }; - - var converter = new ColorSpaceConverter(options); - - // Action - CieXyz actual = converter.Adapt(input, Illuminants.D65); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(22, 33, 1, 22.1090755, 32.2102661, 1.153463)] - public void Adapt_HunterLab_D65_To_D50(float l1, float a1, float b1, float l2, float a2, float b2) - { - // Arrange - var input = new HunterLab(l1, a1, b1, Illuminants.D65); - var expected = new HunterLab(l2, a2, b2); - var options = new ColorSpaceConverterOptions { TargetLabWhitePoint = Illuminants.D50 }; - var converter = new ColorSpaceConverter(options); - - // Action - HunterLab actual = converter.Adapt(input); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - } - - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(22, 33, 1, 22, 33, 0.9999999)] - public void Adapt_CieLchuv_D65_To_D50_XyzScaling(float l1, float c1, float h1, float l2, float c2, float h2) - { - // Arrange - var input = new CieLchuv(l1, c1, h1, Illuminants.D65); - var expected = new CieLchuv(l2, c2, h2); - var options = new ColorSpaceConverterOptions - { - ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling), - TargetLabWhitePoint = Illuminants.D50 - }; - var converter = new ColorSpaceConverter(options); - - // Action - CieLchuv actual = converter.Adapt(input); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - } - - [Theory] - [InlineData(22, 33, 1, 22, 33, 0.9999999)] - public void Adapt_CieLch_D65_To_D50_XyzScaling(float l1, float c1, float h1, float l2, float c2, float h2) - { - // Arrange - var input = new CieLch(l1, c1, h1, Illuminants.D65); - var expected = new CieLch(l2, c2, h2); - var options = new ColorSpaceConverterOptions - { - ChromaticAdaptation = new VonKriesChromaticAdaptation(LmsAdaptationMatrix.XyzScaling), - TargetLabWhitePoint = Illuminants.D50 - }; - var converter = new ColorSpaceConverter(options); - - // Action - CieLch actual = converter.Adapt(input); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCieXyzConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCieXyzConversionTest.cs deleted file mode 100644 index 0119f4f3a..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCieXyzConversionTest.cs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class RgbAndCieXyzConversionTest -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from () - /// to (default sRGB working space). - /// - [Theory] - [InlineData(0.96422, 1.00000, 0.82521, 1, 1, 1)] - [InlineData(0.00000, 1.00000, 0.00000, 0, 1, 0)] - [InlineData(0.96422, 0.00000, 0.00000, 1, 0, 0.292064)] - [InlineData(0.00000, 0.00000, 0.82521, 0, 0.181415, 1)] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.297676, 0.267854, 0.045504, 0.720315, 0.509999, 0.168112)] - public void Convert_XYZ_D50_to_SRGB(float x, float y, float z, float r, float g, float b) - { - // Arrange - var input = new CieXyz(x, y, z); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D50, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; - var converter = new ColorSpaceConverter(options); - var expected = new Rgb(r, g, b); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = converter.ToRgb(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion - /// from () - /// to (default sRGB working space). - /// - [Theory] - [InlineData(0.950470, 1.000000, 1.088830, 1, 1, 1)] - [InlineData(0, 1.000000, 0, 0, 1, 0)] - [InlineData(0.950470, 0, 0, 1, 0, 0.254967)] - [InlineData(0, 0, 1.088830, 0, 0.235458, 1)] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0.297676, 0.267854, 0.045504, 0.754903, 0.501961, 0.099998)] - public void Convert_XYZ_D65_to_SRGB(float x, float y, float z, float r, float g, float b) - { - // Arrange - var input = new CieXyz(x, y, z); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65, TargetRgbWorkingSpace = RgbWorkingSpaces.SRgb }; - var converter = new ColorSpaceConverter(options); - var expected = new Rgb(r, g, b); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = converter.ToRgb(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from (default sRGB working space) - /// to (). - /// - [Theory] - [InlineData(1, 1, 1, 0.964220, 1.000000, 0.825210)] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 0, 0, 0.436075, 0.222504, 0.013932)] - [InlineData(0, 1, 0, 0.385065, 0.716879, 0.0971045)] - [InlineData(0, 0, 1, 0.143080, 0.060617, 0.714173)] - [InlineData(0.754902, 0.501961, 0.100000, 0.315757, 0.273323, 0.035506)] - public void Convert_SRGB_to_XYZ_D50(float r, float g, float b, float x, float y, float z) - { - // Arrange - var input = new Rgb(r, g, b); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D50 }; - var converter = new ColorSpaceConverter(options); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = converter.ToCieXyz(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from (default sRGB working space) - /// to (). - /// - [Theory] - [InlineData(1, 1, 1, 0.950470, 1.000000, 1.088830)] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 0, 0, 0.412456, 0.212673, 0.019334)] - [InlineData(0, 1, 0, 0.357576, 0.715152, 0.119192)] - [InlineData(0, 0, 1, 0.1804375, 0.072175, 0.950304)] - [InlineData(0.754902, 0.501961, 0.100000, 0.297676, 0.267854, 0.045504)] - public void Convert_SRGB_to_XYZ_D65(float r, float g, float b, float x, float y, float z) - { - // Arrange - var input = new Rgb(r, g, b); - var options = new ColorSpaceConverterOptions { WhitePoint = Illuminants.D65 }; - var converter = new ColorSpaceConverter(options); - var expected = new CieXyz(x, y, z); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - // Act - var actual = converter.ToCieXyz(input); - converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCmykConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCmykConversionTest.cs deleted file mode 100644 index 942b34db3..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndCmykConversionTest.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -/// -public class RgbAndCmykConversionTest -{ - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(1, 1, 1, 1, 0, 0, 0)] - [InlineData(0, 0, 0, 0, 1, 1, 1)] - [InlineData(0, 0.84, 0.037, 0.365, 0.635, 0.1016, 0.6115)] - public void Convert_Cmyk_To_Rgb(float c, float m, float y, float k, float r, float g, float b) - { - // Arrange - var input = new Cmyk(c, m, y, k); - var expected = new Rgb(r, g, b); - - Span inputSpan = new Cmyk[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = ColorSpaceConverter.ToRgb(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(1, 1, 1, 0, 0, 0, 0)] - [InlineData(0, 0, 0, 0, 0, 0, 1)] - [InlineData(0.635, 0.1016, 0.6115, 0, 0.84, 0.037, 0.365)] - public void Convert_Rgb_To_Cmyk(float r, float g, float b, float c, float m, float y, float k) - { - // Arrange - var input = new Rgb(r, g, b); - var expected = new Cmyk(c, m, y, k); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new Cmyk[5]; - - // Act - var actual = ColorSpaceConverter.ToCmyk(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.cs deleted file mode 100644 index 8b448a042..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHslConversionTest.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -/// -public class RgbAndHslConversionTest -{ - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0, 1, 1, 1, 1, 1)] - [InlineData(360, 1, 1, 1, 1, 1)] - [InlineData(0, 1, .5F, 1, 0, 0)] - [InlineData(120, 1, .5F, 0, 1, 0)] - [InlineData(240, 1, .5F, 0, 0, 1)] - public void Convert_Hsl_To_Rgb(float h, float s, float l, float r, float g, float b) - { - // Arrange - var input = new Hsl(h, s, l); - var expected = new Rgb(r, g, b); - - Span inputSpan = new Hsl[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = ColorSpaceConverter.ToRgb(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 1, 1, 0, 0, 1)] - [InlineData(1, 0, 0, 0, 1, .5F)] - [InlineData(0, 1, 0, 120, 1, .5F)] - [InlineData(0, 0, 1, 240, 1, .5F)] - [InlineData(0.7, 0.8, 0.6, 90, 0.3333, 0.7F)] - public void Convert_Rgb_To_Hsl(float r, float g, float b, float h, float s, float l) - { - // Arrange - var input = new Rgb(r, g, b); - var expected = new Hsl(h, s, l); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsl[5]; - - // Act - var actual = ColorSpaceConverter.ToHsl(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHsvConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHsvConversionTest.cs deleted file mode 100644 index d80aa6c32..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndHsvConversionTest.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated using: -/// -/// -public class RgbAndHsvConversionTest -{ - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(0, 0, 1, 1, 1, 1)] - [InlineData(360, 1, 1, 1, 0, 0)] - [InlineData(0, 1, 1, 1, 0, 0)] - [InlineData(120, 1, 1, 0, 1, 0)] - [InlineData(240, 1, 1, 0, 0, 1)] - public void Convert_Hsv_To_Rgb(float h, float s, float v, float r, float g, float b) - { - // Arrange - var input = new Hsv(h, s, v); - var expected = new Rgb(r, g, b); - - Span inputSpan = new Hsv[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = ColorSpaceConverter.ToRgb(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 0, 0)] - [InlineData(1, 1, 1, 0, 0, 1)] - [InlineData(1, 0, 0, 0, 1, 1)] - [InlineData(0, 1, 0, 120, 1, 1)] - [InlineData(0, 0, 1, 240, 1, 1)] - public void Convert_Rgb_To_Hsv(float r, float g, float b, float h, float s, float v) - { - // Arrange - var input = new Rgb(r, g, b); - var expected = new Hsv(h, s, v); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new Hsv[5]; - - // Act - var actual = ColorSpaceConverter.ToHsv(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndYCbCrConversionTest.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndYCbCrConversionTest.cs deleted file mode 100644 index eb4eb0bbf..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/RgbAndYCbCrConversionTest.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -/// -/// Tests - conversions. -/// -/// -/// Test data generated mathematically -/// -public class RgbAndYCbCrConversionTest -{ - private static readonly ColorSpaceConverter Converter = new ColorSpaceConverter(); - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.001F); - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(255, 128, 128, 1, 1, 1)] - [InlineData(0, 128, 128, 0, 0, 0)] - [InlineData(128, 128, 128, 0.502, 0.502, 0.502)] - public void Convert_YCbCr_To_Rgb(float y, float cb, float cr, float r, float g, float b) - { - // Arrange - var input = new YCbCr(y, cb, cr); - var expected = new Rgb(r, g, b); - - Span inputSpan = new YCbCr[5]; - inputSpan.Fill(input); - - Span actualSpan = new Rgb[5]; - - // Act - var actual = Converter.ToRgb(input); - Converter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(Rgb.DefaultWorkingSpace, actual.WorkingSpace, ColorSpaceComparer); - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } - - /// - /// Tests conversion from to . - /// - [Theory] - [InlineData(0, 0, 0, 0, 128, 128)] - [InlineData(1, 1, 1, 255, 128, 128)] - [InlineData(0.5, 0.5, 0.5, 127.5, 128, 128)] - [InlineData(1, 0, 0, 76.245, 84.972, 255)] - public void Convert_Rgb_To_YCbCr(float r, float g, float b, float y, float cb, float cr) - { - // Arrange - var input = new Rgb(r, g, b); - var expected = new YCbCr(y, cb, cr); - - Span inputSpan = new Rgb[5]; - inputSpan.Fill(input); - - Span actualSpan = new YCbCr[5]; - - // Act - var actual = ColorSpaceConverter.ToYCbCr(input); - ColorSpaceConverter.Convert(inputSpan, actualSpan); - - // Assert - Assert.Equal(expected, actual, ColorSpaceComparer); - - for (int i = 0; i < actualSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/Conversion/VonKriesChromaticAdaptationTests.cs b/tests/ImageSharp.Tests/Colorspaces/Conversion/VonKriesChromaticAdaptationTests.cs deleted file mode 100644 index 9e33ad168..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/Conversion/VonKriesChromaticAdaptationTests.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces.Conversion; - -public class VonKriesChromaticAdaptationTests -{ - private static readonly ApproximateColorSpaceComparer ColorSpaceComparer = new ApproximateColorSpaceComparer(.0001F); - public static readonly TheoryData WhitePoints = new TheoryData - { - { CieLuv.DefaultWhitePoint, CieLab.DefaultWhitePoint }, - { CieLuv.DefaultWhitePoint, CieLuv.DefaultWhitePoint } - }; - - [Theory] - [MemberData(nameof(WhitePoints))] - public void SingleAndBulkTransformYieldIdenticalResults(CieXyz sourceWhitePoint, CieXyz destinationWhitePoint) - { - var adaptation = new VonKriesChromaticAdaptation(); - var input = new CieXyz(1, 0, 1); - CieXyz expected = adaptation.Transform(input, sourceWhitePoint, destinationWhitePoint); - - Span inputSpan = new CieXyz[5]; - inputSpan.Fill(input); - - Span actualSpan = new CieXyz[5]; - - adaptation.Transform(inputSpan, actualSpan, sourceWhitePoint, destinationWhitePoint); - - for (int i = 0; i < inputSpan.Length; i++) - { - Assert.Equal(expected, actualSpan[i], ColorSpaceComparer); - } - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/HslTests.cs b/tests/ImageSharp.Tests/Colorspaces/HslTests.cs deleted file mode 100644 index a8702488a..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/HslTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class HslTests -{ - [Fact] - public void HslConstructorAssignsFields() - { - const float h = 275F; - const float s = .64F; - const float l = .87F; - var hsl = new Hsl(h, s, l); - - Assert.Equal(h, hsl.H); - Assert.Equal(s, hsl.S); - Assert.Equal(l, hsl.L); - } - - [Fact] - public void HslEquality() - { - var x = default(Hsl); - var y = new Hsl(Vector3.One); - - Assert.True(default(Hsl) == default(Hsl)); - Assert.False(default(Hsl) != default(Hsl)); - Assert.Equal(default(Hsl), default(Hsl)); - Assert.Equal(new Hsl(1, 0, 1), new Hsl(1, 0, 1)); - Assert.Equal(new Hsl(Vector3.One), new Hsl(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/HsvTests.cs b/tests/ImageSharp.Tests/Colorspaces/HsvTests.cs deleted file mode 100644 index caedd3f17..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/HsvTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class HsvTests -{ - [Fact] - public void HsvConstructorAssignsFields() - { - const float h = 275F; - const float s = .64F; - const float v = .87F; - var hsv = new Hsv(h, s, v); - - Assert.Equal(h, hsv.H); - Assert.Equal(s, hsv.S); - Assert.Equal(v, hsv.V); - } - - [Fact] - public void HsvEquality() - { - var x = default(Hsv); - var y = new Hsv(Vector3.One); - - Assert.True(default(Hsv) == default(Hsv)); - Assert.False(default(Hsv) != default(Hsv)); - Assert.Equal(default(Hsv), default(Hsv)); - Assert.Equal(new Hsv(1, 0, 1), new Hsv(1, 0, 1)); - Assert.Equal(new Hsv(Vector3.One), new Hsv(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/HunterLabTests.cs b/tests/ImageSharp.Tests/Colorspaces/HunterLabTests.cs deleted file mode 100644 index 9c97c4c91..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/HunterLabTests.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class HunterLabTests -{ - [Fact] - public void HunterLabConstructorAssignsFields() - { - const float l = 75F; - const float a = -64F; - const float b = 87F; - var hunterLab = new HunterLab(l, a, b); - - Assert.Equal(l, hunterLab.L); - Assert.Equal(a, hunterLab.A); - Assert.Equal(b, hunterLab.B); - } - - [Fact] - public void HunterLabEquality() - { - var x = default(HunterLab); - var y = new HunterLab(Vector3.One); - - Assert.True(default(HunterLab) == default(HunterLab)); - Assert.True(new HunterLab(1, 0, 1) != default(HunterLab)); - Assert.False(new HunterLab(1, 0, 1) == default(HunterLab)); - Assert.Equal(default(HunterLab), default(HunterLab)); - Assert.Equal(new HunterLab(1, 0, 1), new HunterLab(1, 0, 1)); - Assert.Equal(new HunterLab(Vector3.One), new HunterLab(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/LinearRgbTests.cs b/tests/ImageSharp.Tests/Colorspaces/LinearRgbTests.cs deleted file mode 100644 index ff2d15134..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/LinearRgbTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class LinearRgbTests -{ - [Fact] - public void LinearRgbConstructorAssignsFields() - { - const float r = .75F; - const float g = .64F; - const float b = .87F; - var rgb = new LinearRgb(r, g, b); - - Assert.Equal(r, rgb.R); - Assert.Equal(g, rgb.G); - Assert.Equal(b, rgb.B); - } - - [Fact] - public void LinearRgbEquality() - { - var x = default(LinearRgb); - var y = new LinearRgb(Vector3.One); - - Assert.True(default(LinearRgb) == default(LinearRgb)); - Assert.False(default(LinearRgb) != default(LinearRgb)); - Assert.Equal(default(LinearRgb), default(LinearRgb)); - Assert.Equal(new LinearRgb(1, 0, 1), new LinearRgb(1, 0, 1)); - Assert.Equal(new LinearRgb(Vector3.One), new LinearRgb(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/LmsTests.cs b/tests/ImageSharp.Tests/Colorspaces/LmsTests.cs deleted file mode 100644 index 5e8840664..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/LmsTests.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class LmsTests -{ - [Fact] - public void LmsConstructorAssignsFields() - { - const float l = 75F; - const float m = -64F; - const float s = 87F; - var lms = new Lms(l, m, s); - - Assert.Equal(l, lms.L); - Assert.Equal(m, lms.M); - Assert.Equal(s, lms.S); - } - - [Fact] - public void LmsEquality() - { - var x = default(Lms); - var y = new Lms(Vector3.One); - - Assert.True(default(Lms) == default(Lms)); - Assert.True(new Lms(1, 0, 1) != default(Lms)); - Assert.False(new Lms(1, 0, 1) == default(Lms)); - Assert.Equal(default(Lms), default(Lms)); - Assert.Equal(new Lms(1, 0, 1), new Lms(1, 0, 1)); - Assert.Equal(new Lms(Vector3.One), new Lms(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/RgbTests.cs b/tests/ImageSharp.Tests/Colorspaces/RgbTests.cs deleted file mode 100644 index be96b79f4..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/RgbTests.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.PixelFormats; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class RgbTests -{ - [Fact] - public void RgbConstructorAssignsFields() - { - const float r = .75F; - const float g = .64F; - const float b = .87F; - var rgb = new Rgb(r, g, b); - - Assert.Equal(r, rgb.R); - Assert.Equal(g, rgb.G); - Assert.Equal(b, rgb.B); - } - - [Fact] - public void RgbEquality() - { - var x = default(Rgb); - var y = new Rgb(Vector3.One); - - Assert.True(default(Rgb) == default(Rgb)); - Assert.False(default(Rgb) != default(Rgb)); - Assert.Equal(default(Rgb), default(Rgb)); - Assert.Equal(new Rgb(1, 0, 1), new Rgb(1, 0, 1)); - Assert.Equal(new Rgb(Vector3.One), new Rgb(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } - - [Fact] - public void RgbAndRgb24Operators() - { - const byte r = 64; - const byte g = 128; - const byte b = 255; - - Rgb24 rgb24 = new Rgb(r / 255F, g / 255F, b / 255F); - Rgb rgb2 = rgb24; - - Assert.Equal(r, rgb24.R); - Assert.Equal(g, rgb24.G); - Assert.Equal(b, rgb24.B); - - Assert.Equal(r / 255F, rgb2.R); - Assert.Equal(g / 255F, rgb2.G); - Assert.Equal(b / 255F, rgb2.B); - } - - [Fact] - public void RgbAndRgba32Operators() - { - const byte r = 64; - const byte g = 128; - const byte b = 255; - - Rgba32 rgba32 = new Rgb(r / 255F, g / 255F, b / 255F); - Rgb rgb2 = rgba32; - - Assert.Equal(r, rgba32.R); - Assert.Equal(g, rgba32.G); - Assert.Equal(b, rgba32.B); - - Assert.Equal(r / 255F, rgb2.R); - Assert.Equal(g / 255F, rgb2.G); - Assert.Equal(b / 255F, rgb2.B); - } -} diff --git a/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs b/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs deleted file mode 100644 index 2ae0824f2..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/StringRepresentationTests.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; -using SixLabors.ImageSharp.ColorSpaces.Conversion; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -public class StringRepresentationTests -{ - private static readonly Vector3 One = new Vector3(1); - private static readonly Vector3 Zero = new Vector3(0); - private static readonly Vector3 Random = new Vector3(42.4F, 94.5F, 83.4F); - - public static readonly TheoryData TestData = new TheoryData - { - { new CieLab(Zero), "CieLab(0, 0, 0)" }, - { new CieLch(Zero), "CieLch(0, 0, 0)" }, - { new CieLchuv(Zero), "CieLchuv(0, 0, 0)" }, - { new CieLuv(Zero), "CieLuv(0, 0, 0)" }, - { new CieXyz(Zero), "CieXyz(0, 0, 0)" }, - { new CieXyy(Zero), "CieXyy(0, 0, 0)" }, - { new HunterLab(Zero), "HunterLab(0, 0, 0)" }, - { new Lms(Zero), "Lms(0, 0, 0)" }, - { new LinearRgb(Zero), "LinearRgb(0, 0, 0)" }, - { new Rgb(Zero), "Rgb(0, 0, 0)" }, - { new Hsl(Zero), "Hsl(0, 0, 0)" }, - { new Hsv(Zero), "Hsv(0, 0, 0)" }, - { new YCbCr(Zero), "YCbCr(0, 0, 0)" }, - { new CieLab(One), "CieLab(1, 1, 1)" }, - { new CieLch(One), "CieLch(1, 1, 1)" }, - { new CieLchuv(One), "CieLchuv(1, 1, 1)" }, - { new CieLuv(One), "CieLuv(1, 1, 1)" }, - { new CieXyz(One), "CieXyz(1, 1, 1)" }, - { new CieXyy(One), "CieXyy(1, 1, 1)" }, - { new HunterLab(One), "HunterLab(1, 1, 1)" }, - { new Lms(One), "Lms(1, 1, 1)" }, - { new LinearRgb(One), "LinearRgb(1, 1, 1)" }, - { new Rgb(One), "Rgb(1, 1, 1)" }, - { new Hsl(One), "Hsl(1, 1, 1)" }, - { new Hsv(One), "Hsv(1, 1, 1)" }, - { new YCbCr(One), "YCbCr(1, 1, 1)" }, - { new CieXyChromaticityCoordinates(1, 1), "CieXyChromaticityCoordinates(1, 1)" }, - { new CieLab(Random), "CieLab(42.4, 94.5, 83.4)" }, - { new CieLch(Random), "CieLch(42.4, 94.5, 83.4)" }, - { new CieLchuv(Random), "CieLchuv(42.4, 94.5, 83.4)" }, - { new CieLuv(Random), "CieLuv(42.4, 94.5, 83.4)" }, - { new CieXyz(Random), "CieXyz(42.4, 94.5, 83.4)" }, - { new CieXyy(Random), "CieXyy(42.4, 94.5, 83.4)" }, - { new HunterLab(Random), "HunterLab(42.4, 94.5, 83.4)" }, - { new Lms(Random), "Lms(42.4, 94.5, 83.4)" }, - { new LinearRgb(Random), "LinearRgb(1, 1, 1)" }, // clamping to 1 is expected - { new Rgb(Random), "Rgb(1, 1, 1)" }, // clamping to 1 is expected - { new Hsl(Random), "Hsl(42.4, 1, 1)" }, // clamping to 1 is expected - { new Hsv(Random), "Hsv(42.4, 1, 1)" }, // clamping to 1 is expected - { new YCbCr(Random), "YCbCr(42.4, 94.5, 83.4)" }, - }; - - [Theory] - [MemberData(nameof(TestData))] - public void StringRepresentationsAreCorrect(object color, string text) => Assert.Equal(text, color.ToString()); -} diff --git a/tests/ImageSharp.Tests/Colorspaces/YCbCrTests.cs b/tests/ImageSharp.Tests/Colorspaces/YCbCrTests.cs deleted file mode 100644 index efab45c3c..000000000 --- a/tests/ImageSharp.Tests/Colorspaces/YCbCrTests.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -using System.Numerics; -using SixLabors.ImageSharp.ColorSpaces; - -namespace SixLabors.ImageSharp.Tests.Colorspaces; - -/// -/// Tests the struct. -/// -public class YCbCrTests -{ - [Fact] - public void YCbCrConstructorAssignsFields() - { - const float y = 75F; - const float cb = 64F; - const float cr = 87F; - var yCbCr = new YCbCr(y, cb, cr); - - Assert.Equal(y, yCbCr.Y); - Assert.Equal(cb, yCbCr.Cb); - Assert.Equal(cr, yCbCr.Cr); - } - - [Fact] - public void YCbCrEquality() - { - var x = default(YCbCr); - var y = new YCbCr(Vector3.One); - - Assert.True(default(YCbCr) == default(YCbCr)); - Assert.False(default(YCbCr) != default(YCbCr)); - Assert.Equal(default(YCbCr), default(YCbCr)); - Assert.Equal(new YCbCr(1, 0, 1), new YCbCr(1, 0, 1)); - Assert.Equal(new YCbCr(Vector3.One), new YCbCr(Vector3.One)); - Assert.False(x.Equals(y)); - Assert.False(x.Equals((object)y)); - Assert.False(x.GetHashCode().Equals(y.GetHashCode())); - } -} From b458ff2fa024ae478594aa03a8a70f0a6cdb7e4c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 21 May 2024 15:48:23 +1000 Subject: [PATCH 43/79] Optimize and cleanup per review --- src/ImageSharp/ColorProfiles/CieLab.cs | 18 ++------ src/ImageSharp/ColorProfiles/CieLch.cs | 10 ++-- src/ImageSharp/ColorProfiles/CieLchuv.cs | 18 ++------ src/ImageSharp/ColorProfiles/CieLuv.cs | 10 ++-- .../CieXyChromaticityCoordinates.cs | 7 +-- src/ImageSharp/ColorProfiles/CieXyy.cs | 10 ++-- src/ImageSharp/ColorProfiles/CieXyz.cs | 5 +- src/ImageSharp/ColorProfiles/Cmyk.cs | 13 ++---- src/ImageSharp/ColorProfiles/Hsl.cs | 10 ++-- src/ImageSharp/ColorProfiles/Hsv.cs | 10 ++-- src/ImageSharp/ColorProfiles/HunterLab.cs | 22 ++++----- .../ColorProfiles/KnownIlluminants.cs | 46 +++++++++---------- src/ImageSharp/ColorProfiles/Lms.cs | 14 +++--- src/ImageSharp/ColorProfiles/Rgb.cs | 10 ++-- .../RgbPrimariesChromaticityCoordinates.cs | 2 +- src/ImageSharp/ColorProfiles/YCbCr.cs | 10 ++-- 16 files changed, 87 insertions(+), 128 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs index 72148af45..82d60f4da 100644 --- a/src/ImageSharp/ColorProfiles/CieLab.cs +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -12,12 +12,6 @@ namespace SixLabors.ImageSharp.ColorProfiles; ///
public readonly struct CieLab : IProfileConnectingSpace { - /// - /// D50 standard illuminant. - /// Used when reference white is not specified explicitly. - /// - public static readonly CieXyz DefaultWhitePoint = KnownIlluminants.D50; - /// /// Initializes a new instance of the struct. /// @@ -50,19 +44,19 @@ public readonly struct CieLab : IProfileConnectingSpace /// Gets the lightness dimension. /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white). ///
- public readonly float L { get; } + public float L { get; } /// /// Gets the a color component. /// A value usually ranging from -100 to 100. Negative is green, positive magenta. /// - public readonly float A { get; } + public float A { get; } /// /// Gets the b color component. /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow /// - public readonly float B { get; } + public float B { get; } /// /// Compares two objects for equality. @@ -97,10 +91,8 @@ public readonly struct CieLab : IProfileConnectingSpace /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(CieLab other) => - this.L.Equals(other.L) - && this.A.Equals(other.A) - && this.B.Equals(other.B); + public bool Equals(CieLab other) + => new Vector3(this.L, this.A, this.B) == new Vector3(other.L, other.A, other.B); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/ColorProfiles/CieLch.cs b/src/ImageSharp/ColorProfiles/CieLch.cs index a8d70cfb7..8f6298a2d 100644 --- a/src/ImageSharp/ColorProfiles/CieLch.cs +++ b/src/ImageSharp/ColorProfiles/CieLch.cs @@ -44,19 +44,19 @@ public readonly struct CieLch : IColorProfile /// Gets the lightness dimension. /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). /// - public readonly float L { get; } + public float L { get; } /// /// Gets the a chroma component. /// A value ranging from 0 to 200. /// - public readonly float C { get; } + public float C { get; } /// /// Gets the h° hue component in degrees. /// A value ranging from 0 to 360. /// - public readonly float H { get; } + public float H { get; } /// /// Compares two objects for equality. @@ -94,9 +94,7 @@ public readonly struct CieLch : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieLch other) - => this.L.Equals(other.L) - && this.C.Equals(other.C) - && this.H.Equals(other.H); + => new Vector3(this.L, this.C, this.H) == new Vector3(other.L, other.C, other.H); /// public static CieLch FromProfileConnectingSpace(ColorConversionOptions options, in CieLab source) diff --git a/src/ImageSharp/ColorProfiles/CieLchuv.cs b/src/ImageSharp/ColorProfiles/CieLchuv.cs index f8f62d104..e537259e4 100644 --- a/src/ImageSharp/ColorProfiles/CieLchuv.cs +++ b/src/ImageSharp/ColorProfiles/CieLchuv.cs @@ -45,24 +45,19 @@ public readonly struct CieLchuv : IColorProfile /// Gets the lightness dimension. /// A value ranging between 0 (black), 100 (diffuse white) or higher (specular white). /// - public readonly float L { get; } + public float L { get; } /// /// Gets the a chroma component. /// A value ranging from 0 to 200. /// - public readonly float C { get; } + public float C { get; } /// /// Gets the h° hue component in degrees. /// A value ranging from 0 to 360. /// - public readonly float H { get; } - - /// - /// Gets the reference white point of this color - /// - public readonly CieXyz WhitePoint { get; } + public float H { get; } /// /// Compares two objects for equality. @@ -151,7 +146,7 @@ public readonly struct CieLchuv : IColorProfile /// public override int GetHashCode() - => HashCode.Combine(this.L, this.C, this.H, this.WhitePoint); + => HashCode.Combine(this.L, this.C, this.H); /// public override string ToString() @@ -164,10 +159,7 @@ public readonly struct CieLchuv : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieLchuv other) - => this.L.Equals(other.L) - && this.C.Equals(other.C) - && this.H.Equals(other.H) - && this.WhitePoint.Equals(other.WhitePoint); + => new Vector3(this.L, this.C, this.H) == new Vector3(other.L, other.C, other.H); /// /// Computes the saturation of the color (chroma normalized by lightness) diff --git a/src/ImageSharp/ColorProfiles/CieLuv.cs b/src/ImageSharp/ColorProfiles/CieLuv.cs index 9bf102057..118d32f04 100644 --- a/src/ImageSharp/ColorProfiles/CieLuv.cs +++ b/src/ImageSharp/ColorProfiles/CieLuv.cs @@ -46,19 +46,19 @@ public readonly struct CieLuv : IColorProfile /// Gets the lightness dimension /// A value usually ranging between 0 and 100. /// - public readonly float L { get; } + public float L { get; } /// /// Gets the blue-yellow chromaticity coordinate of the given white point. /// A value usually ranging between -100 and 100. /// - public readonly float U { get; } + public float U { get; } /// /// Gets the red-green chromaticity coordinate of the given white point. /// A value usually ranging between -100 and 100. /// - public readonly float V { get; } + public float V { get; } /// /// Compares two objects for equality. @@ -205,9 +205,7 @@ public readonly struct CieLuv : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieLuv other) - => this.L.Equals(other.L) - && this.U.Equals(other.U) - && this.V.Equals(other.V); + => new Vector3(this.L, this.U, this.V) == new Vector3(other.L, other.U, other.V); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double ComputeU(in CieXyz source) diff --git a/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs index b55abdd05..aad7dbf95 100644 --- a/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using System.Runtime.CompilerServices; // ReSharper disable CompareOfFloatsByEqualityOperator @@ -29,7 +30,7 @@ public readonly struct CieXyChromaticityCoordinates : IEquatable /// Ranges usually from 0 to 1. /// - public readonly float X { get; } + public float X { get; } /// /// Gets the chromaticity Y-coordinate @@ -37,7 +38,7 @@ public readonly struct CieXyChromaticityCoordinates : IEquatable /// Ranges usually from 0 to 1. /// - public readonly float Y { get; } + public float Y { get; } /// /// Compares two objects for equality. @@ -79,5 +80,5 @@ public readonly struct CieXyChromaticityCoordinates : IEquatable [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(CieXyChromaticityCoordinates other) - => this.X.Equals(other.X) && this.Y.Equals(other.Y); + => new Vector2(this.X, this.Y) == new Vector2(other.X, other.Y); } diff --git a/src/ImageSharp/ColorProfiles/CieXyy.cs b/src/ImageSharp/ColorProfiles/CieXyy.cs index 4dcb582d6..ea19f5740 100644 --- a/src/ImageSharp/ColorProfiles/CieXyy.cs +++ b/src/ImageSharp/ColorProfiles/CieXyy.cs @@ -45,19 +45,19 @@ public readonly struct CieXyy : IColorProfile /// Gets the X chrominance component. /// A value usually ranging between 0 and 1. /// - public readonly float X { get; } + public float X { get; } /// /// Gets the Y chrominance component. /// A value usually ranging between 0 and 1. /// - public readonly float Y { get; } + public float Y { get; } /// /// Gets the Y luminance component. /// A value usually ranging between 0 and 1. /// - public readonly float Yl { get; } + public float Yl { get; } /// /// Compares two objects for equality. @@ -150,7 +150,5 @@ public readonly struct CieXyy : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieXyy other) - => this.X.Equals(other.X) - && this.Y.Equals(other.Y) - && this.Yl.Equals(other.Yl); + => new Vector3(this.X, this.Y, this.Yl) == new Vector3(other.X, other.Y, other.Yl); } diff --git a/src/ImageSharp/ColorProfiles/CieXyz.cs b/src/ImageSharp/ColorProfiles/CieXyz.cs index fbd3e77d7..fe8755454 100644 --- a/src/ImageSharp/ColorProfiles/CieXyz.cs +++ b/src/ImageSharp/ColorProfiles/CieXyz.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; namespace SixLabors.ImageSharp.ColorProfiles; @@ -99,9 +98,7 @@ public readonly struct CieXyz : IProfileConnectingSpace /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieXyz other) - => this.X.Equals(other.X) - && this.Y.Equals(other.Y) - && this.Z.Equals(other.Z); + => new Vector3(this.X, this.Y, this.Z) == new Vector3(other.X, other.Y, other.Z); /// public static CieXyz FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) diff --git a/src/ImageSharp/ColorProfiles/Cmyk.cs b/src/ImageSharp/ColorProfiles/Cmyk.cs index d586ab6d4..88d3249b3 100644 --- a/src/ImageSharp/ColorProfiles/Cmyk.cs +++ b/src/ImageSharp/ColorProfiles/Cmyk.cs @@ -45,25 +45,25 @@ public readonly struct Cmyk : IColorProfile /// Gets the cyan color component. /// A value ranging between 0 and 1. /// - public readonly float C { get; } + public float C { get; } /// /// Gets the magenta color component. /// A value ranging between 0 and 1. /// - public readonly float M { get; } + public float M { get; } /// /// Gets the yellow color component. /// A value ranging between 0 and 1. /// - public readonly float Y { get; } + public float Y { get; } /// /// Gets the keyline black color component. /// A value ranging between 0 and 1. /// - public readonly float K { get; } + public float K { get; } /// /// Compares two objects for equality. @@ -157,8 +157,5 @@ public readonly struct Cmyk : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Cmyk other) - => this.C.Equals(other.C) - && this.M.Equals(other.M) - && this.Y.Equals(other.Y) - && this.K.Equals(other.K); + => new Vector4(this.C, this.M, this.Y, this.K) == new Vector4(other.C, other.M, other.Y, other.K); } diff --git a/src/ImageSharp/ColorProfiles/Hsl.cs b/src/ImageSharp/ColorProfiles/Hsl.cs index 8dcef23c4..c7ae97c79 100644 --- a/src/ImageSharp/ColorProfiles/Hsl.cs +++ b/src/ImageSharp/ColorProfiles/Hsl.cs @@ -43,19 +43,19 @@ public readonly struct Hsl : IColorProfile /// Gets the hue component. /// A value ranging between 0 and 360. /// - public readonly float H { get; } + public float H { get; } /// /// Gets the saturation component. /// A value ranging between 0 and 1. /// - public readonly float S { get; } + public float S { get; } /// /// Gets the lightness component. /// A value ranging between 0 and 1. /// - public readonly float L { get; } + public float L { get; } /// /// Compares two objects for equality. @@ -200,9 +200,7 @@ public readonly struct Hsl : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Hsl other) - => this.H.Equals(other.H) - && this.S.Equals(other.S) - && this.L.Equals(other.L); + => new Vector3(this.H, this.S, this.L) == new Vector3(other.H, other.S, other.L); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float GetColorComponent(float first, float second, float third) diff --git a/src/ImageSharp/ColorProfiles/Hsv.cs b/src/ImageSharp/ColorProfiles/Hsv.cs index b3922d11f..83445a40f 100644 --- a/src/ImageSharp/ColorProfiles/Hsv.cs +++ b/src/ImageSharp/ColorProfiles/Hsv.cs @@ -43,19 +43,19 @@ public readonly struct Hsv : IColorProfile /// Gets the hue component. /// A value ranging between 0 and 360. /// - public readonly float H { get; } + public float H { get; } /// /// Gets the saturation component. /// A value ranging between 0 and 1. /// - public readonly float S { get; } + public float S { get; } /// /// Gets the value (brightness) component. /// A value ranging between 0 and 1. /// - public readonly float V { get; } + public float V { get; } /// /// Compares two objects for equality. @@ -223,7 +223,5 @@ public readonly struct Hsv : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Hsv other) - => this.H.Equals(other.H) - && this.S.Equals(other.S) - && this.V.Equals(other.V); + => new Vector3(this.H, this.S, this.V) == new Vector3(other.H, other.S, other.V); } diff --git a/src/ImageSharp/ColorProfiles/HunterLab.cs b/src/ImageSharp/ColorProfiles/HunterLab.cs index 5d6c01a2b..eb33e6a95 100644 --- a/src/ImageSharp/ColorProfiles/HunterLab.cs +++ b/src/ImageSharp/ColorProfiles/HunterLab.cs @@ -20,8 +20,10 @@ public readonly struct HunterLab : IColorProfile /// The b (blue - yellow) component. [MethodImpl(MethodImplOptions.AggressiveInlining)] public HunterLab(float l, float a, float b) - : this(new Vector3(l, a, b)) { + this.L = l; + this.A = a; + this.B = b; } /// @@ -41,24 +43,19 @@ public readonly struct HunterLab : IColorProfile /// Gets the lightness dimension. /// A value usually ranging between 0 (black), 100 (diffuse white) or higher (specular white). /// - public readonly float L { get; } + public float L { get; } /// /// Gets the a color component. /// A value usually ranging from -100 to 100. Negative is green, positive magenta. /// - public readonly float A { get; } + public float A { get; } /// /// Gets the b color component. /// A value usually ranging from -100 to 100. Negative is blue, positive is yellow /// - public readonly float B { get; } - - /// - /// Gets the reference white point of this color. - /// - public readonly CieXyz WhitePoint { get; } + public float B { get; } /// /// Compares two objects for equality. @@ -162,7 +159,7 @@ public readonly struct HunterLab : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B, this.WhitePoint); + public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B); /// public override string ToString() => FormattableString.Invariant($"HunterLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); @@ -173,10 +170,7 @@ public readonly struct HunterLab : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(HunterLab other) - => this.L.Equals(other.L) - && this.A.Equals(other.A) - && this.B.Equals(other.B) - && this.WhitePoint.Equals(other.WhitePoint); + => new Vector3(this.L, this.A, this.B) == new Vector3(other.L, other.A, other.B); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float ComputeKa(in CieXyz whitePoint) diff --git a/src/ImageSharp/ColorProfiles/KnownIlluminants.cs b/src/ImageSharp/ColorProfiles/KnownIlluminants.cs index ac5cbea2c..b9236497f 100644 --- a/src/ImageSharp/ColorProfiles/KnownIlluminants.cs +++ b/src/ImageSharp/ColorProfiles/KnownIlluminants.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.ColorProfiles; @@ -15,57 +15,57 @@ namespace SixLabors.ImageSharp.ColorProfiles; public static class KnownIlluminants { /// - /// Incandescent / Tungsten + /// Gets the Incandescent / Tungsten illuminant. /// - public static readonly CieXyz A = new(1.09850F, 1F, 0.35585F); + public static CieXyz A { get; } = new(1.09850F, 1F, 0.35585F); /// - /// Direct sunlight at noon (obsoleteF) + /// Gets the Direct sunlight at noon (obsoleteF) illuminant. /// - public static readonly CieXyz B = new(0.99072F, 1F, 0.85223F); + public static CieXyz B { get; } = new(0.99072F, 1F, 0.85223F); /// - /// Average / North sky Daylight (obsoleteF) + /// Gets the Average / North sky Daylight (obsoleteF) illuminant. /// - public static readonly CieXyz C = new(0.98074F, 1F, 1.18232F); + public static CieXyz C { get; } = new(0.98074F, 1F, 1.18232F); /// - /// Horizon Light. ICC profile PCS + /// Gets the Horizon Light. ICC profile PCS illuminant. /// - public static readonly CieXyz D50 = new(0.96422F, 1F, 0.82521F); + public static CieXyz D50 { get; } = new(0.96422F, 1F, 0.82521F); /// - /// Mid-morning / Mid-afternoon Daylight + /// Gets the Mid-morning / Mid-afternoon Daylight illuminant. /// - public static readonly CieXyz D55 = new(0.95682F, 1F, 0.92149F); + public static CieXyz D55 { get; } = new(0.95682F, 1F, 0.92149F); /// - /// Noon Daylight: TelevisionF, sRGB color space + /// Gets the Noon Daylight: TelevisionF, sRGB color space illuminant. /// - public static readonly CieXyz D65 = new(0.95047F, 1F, 1.08883F); + public static CieXyz D65 { get; } = new(0.95047F, 1F, 1.08883F); /// - /// North sky Daylight + /// Gets the North sky Daylight illuminant. /// - public static readonly CieXyz D75 = new(0.94972F, 1F, 1.22638F); + public static CieXyz D75 { get; } = new(0.94972F, 1F, 1.22638F); /// - /// Equal energy + /// Gets the Equal energy illuminant. /// - public static readonly CieXyz E = new(1F, 1F, 1F); + public static CieXyz E { get; } = new(1F, 1F, 1F); /// - /// Cool White Fluorescent + /// Gets the Cool White Fluorescent illuminant. /// - public static readonly CieXyz F2 = new(0.99186F, 1F, 0.67393F); + public static CieXyz F2 { get; } = new(0.99186F, 1F, 0.67393F); /// - /// D65 simulatorF, Daylight simulator + /// Gets the D65 simulatorF, Daylight simulator illuminant. /// - public static readonly CieXyz F7 = new(0.95041F, 1F, 1.08747F); + public static CieXyz F7 { get; } = new(0.95041F, 1F, 1.08747F); /// - /// Philips TL84F, Ultralume 40 + /// Gets the Philips TL84F, Ultralume 40 illuminant. /// - public static readonly CieXyz F11 = new(1.00962F, 1F, 0.64350F); + public static CieXyz F11 { get; } = new(1.00962F, 1F, 0.64350F); } diff --git a/src/ImageSharp/ColorProfiles/Lms.cs b/src/ImageSharp/ColorProfiles/Lms.cs index cb060185d..03a1c5d66 100644 --- a/src/ImageSharp/ColorProfiles/Lms.cs +++ b/src/ImageSharp/ColorProfiles/Lms.cs @@ -16,8 +16,10 @@ internal readonly struct Lms : IColorProfile /// S represents the responsivity at short wavelengths. [MethodImpl(MethodImplOptions.AggressiveInlining)] public Lms(float l, float m, float s) - : this(new Vector3(l, m, s)) { + this.L = l; + this.M = m; + this.S = s; } /// @@ -37,19 +39,19 @@ internal readonly struct Lms : IColorProfile /// Gets the L long component. /// A value usually ranging between -1 and 1. /// - public readonly float L { get; } + public float L { get; } /// /// Gets the M medium component. /// A value usually ranging between -1 and 1. /// - public readonly float M { get; } + public float M { get; } /// /// Gets the S short component. /// A value usually ranging between -1 and 1. /// - public readonly float S { get; } + public float S { get; } /// /// Compares two objects for equality. @@ -92,9 +94,7 @@ internal readonly struct Lms : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Lms other) - => this.L.Equals(other.L) - && this.M.Equals(other.M) - && this.S.Equals(other.S); + => new Vector3(this.L, this.M, this.S) == new Vector3(other.L, other.M, other.S); /// public static Lms FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs index 697c0fbd8..8036d83e5 100644 --- a/src/ImageSharp/ColorProfiles/Rgb.cs +++ b/src/ImageSharp/ColorProfiles/Rgb.cs @@ -43,19 +43,19 @@ public readonly struct Rgb : IProfileConnectingSpace /// Gets the red component. /// A value usually ranging between 0 and 1. /// - public readonly float R { get; } + public float R { get; } /// /// Gets the green component. /// A value usually ranging between 0 and 1. /// - public readonly float G { get; } + public float G { get; } /// /// Gets the blue component. /// A value usually ranging between 0 and 1. /// - public readonly float B { get; } + public float B { get; } /// /// Compares two objects for equality. @@ -91,9 +91,7 @@ public readonly struct Rgb : IProfileConnectingSpace /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Rgb other) - => this.R.Equals(other.R) - && this.G.Equals(other.G) - && this.B.Equals(other.B); + => new Vector3(this.R, this.G, this.B) == new Vector3(other.R, other.G, other.B); /// public static Rgb FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) diff --git a/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs b/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs index 90a5a57f9..1040f23ac 100644 --- a/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorProfiles/RgbPrimariesChromaticityCoordinates.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; diff --git a/src/ImageSharp/ColorProfiles/YCbCr.cs b/src/ImageSharp/ColorProfiles/YCbCr.cs index 5f0b4118a..b9f5f65ee 100644 --- a/src/ImageSharp/ColorProfiles/YCbCr.cs +++ b/src/ImageSharp/ColorProfiles/YCbCr.cs @@ -45,19 +45,19 @@ public readonly struct YCbCr : IColorProfile /// Gets the Y luminance component. /// A value ranging between 0 and 255. /// - public readonly float Y { get; } + public float Y { get; } /// /// Gets the Cb chroma component. /// A value ranging between 0 and 255. /// - public readonly float Cb { get; } + public float Cb { get; } /// /// Gets the Cr chroma component. /// A value ranging between 0 and 255. /// - public readonly float Cr { get; } + public float Cr { get; } /// /// Compares two objects for equality. @@ -152,7 +152,5 @@ public readonly struct YCbCr : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(YCbCr other) - => this.Y.Equals(other.Y) - && this.Cb.Equals(other.Cb) - && this.Cr.Equals(other.Cr); + => new Vector3(this.Y, this.Cb, this.Cr) == new Vector3(other.Y, other.Cb, other.Cr); } From ff752a22b80638396287bb5c68637b72c5c73f61 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 22 May 2024 19:34:53 +1000 Subject: [PATCH 44/79] Sequential layout plus faster equality checks. --- src/ImageSharp/ColorProfiles/CieLab.cs | 32 ++++++++------- src/ImageSharp/ColorProfiles/CieLch.cs | 36 +++++++++-------- src/ImageSharp/ColorProfiles/CieLchuv.cs | 24 ++---------- src/ImageSharp/ColorProfiles/CieLuv.cs | 6 ++- .../CieXyChromaticityCoordinates.cs | 7 +++- src/ImageSharp/ColorProfiles/CieXyy.cs | 6 ++- src/ImageSharp/ColorProfiles/CieXyz.cs | 32 ++++++++------- src/ImageSharp/ColorProfiles/Cmyk.cs | 6 ++- src/ImageSharp/ColorProfiles/Hsl.cs | 6 ++- src/ImageSharp/ColorProfiles/Hsv.cs | 6 ++- src/ImageSharp/ColorProfiles/HunterLab.cs | 6 ++- src/ImageSharp/ColorProfiles/Lms.cs | 39 ++++++++++++------- src/ImageSharp/ColorProfiles/Rgb.cs | 32 ++++++++------- src/ImageSharp/ColorProfiles/YCbCr.cs | 6 ++- 14 files changed, 142 insertions(+), 102 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/CieLab.cs b/src/ImageSharp/ColorProfiles/CieLab.cs index 82d60f4da..377cc20a9 100644 --- a/src/ImageSharp/ColorProfiles/CieLab.cs +++ b/src/ImageSharp/ColorProfiles/CieLab.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -10,6 +11,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// Represents a CIE L*a*b* 1976 color. /// /// +[StructLayout(LayoutKind.Sequential)] public readonly struct CieLab : IProfileConnectingSpace { /// @@ -80,20 +82,6 @@ public readonly struct CieLab : IProfileConnectingSpace [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(CieLab left, CieLab right) => !left.Equals(right); - /// - public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B); - - /// - public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is CieLab other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(CieLab other) - => new Vector3(this.L, this.A, this.B) == new Vector3(other.L, other.A, other.B); - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static CieLab FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) @@ -171,4 +159,20 @@ public readonly struct CieLab : IProfileConnectingSpace /// public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + public override int GetHashCode() => HashCode.Combine(this.L, this.A, this.B); + + /// + public override string ToString() => FormattableString.Invariant($"CieLab({this.L:#0.##}, {this.A:#0.##}, {this.B:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is CieLab other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieLab other) + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/CieLch.cs b/src/ImageSharp/ColorProfiles/CieLch.cs index 8f6298a2d..131978340 100644 --- a/src/ImageSharp/ColorProfiles/CieLch.cs +++ b/src/ImageSharp/ColorProfiles/CieLch.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -10,6 +11,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// Represents the CIE L*C*h°, cylindrical form of the CIE L*a*b* 1976 color. /// /// +[StructLayout(LayoutKind.Sequential)] public readonly struct CieLch : IColorProfile { private static readonly Vector3 Min = new(0, -200, 0); @@ -80,22 +82,6 @@ public readonly struct CieLch : IColorProfile [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(CieLch left, CieLch right) => !left.Equals(right); - /// - public override int GetHashCode() - => HashCode.Combine(this.L, this.C, this.H); - - /// - public override string ToString() => FormattableString.Invariant($"CieLch({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(object? obj) => obj is CieLch other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(CieLch other) - => new Vector3(this.L, this.C, this.H) == new Vector3(other.L, other.C, other.H); - /// public static CieLch FromProfileConnectingSpace(ColorConversionOptions options, in CieLab source) { @@ -159,4 +145,22 @@ public readonly struct CieLch : IColorProfile /// public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + public override int GetHashCode() + => HashCode.Combine(this.L, this.C, this.H); + + /// + public override string ToString() => FormattableString.Invariant($"CieLch({this.L:#0.##}, {this.C:#0.##}, {this.H:#0.##})"); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object? obj) => obj is CieLch other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieLch other) + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/CieLchuv.cs b/src/ImageSharp/ColorProfiles/CieLchuv.cs index e537259e4..7fd95feb1 100644 --- a/src/ImageSharp/ColorProfiles/CieLchuv.cs +++ b/src/ImageSharp/ColorProfiles/CieLchuv.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -10,6 +11,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// Represents the CIE L*C*h°, cylindrical form of the CIE L*u*v* 1976 color. /// /// +[StructLayout(LayoutKind.Sequential)] public readonly struct CieLchuv : IColorProfile { private static readonly Vector3 Min = new(0, -200, 0); @@ -159,25 +161,7 @@ public readonly struct CieLchuv : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieLchuv other) - => new Vector3(this.L, this.C, this.H) == new Vector3(other.L, other.C, other.H); + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); - /// - /// Computes the saturation of the color (chroma normalized by lightness) - /// - /// - /// A value ranging from 0 to 100. - /// - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public float Saturation() - { - float result = 100 * (this.C / this.L); - - if (float.IsNaN(result)) - { - return 0; - } - - return result; - } + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/CieLuv.cs b/src/ImageSharp/ColorProfiles/CieLuv.cs index 118d32f04..97e2826f7 100644 --- a/src/ImageSharp/ColorProfiles/CieLuv.cs +++ b/src/ImageSharp/ColorProfiles/CieLuv.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -12,6 +13,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// attempted perceptual uniformity /// /// +[StructLayout(LayoutKind.Sequential)] public readonly struct CieLuv : IColorProfile { /// @@ -205,7 +207,9 @@ public readonly struct CieLuv : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieLuv other) - => new Vector3(this.L, this.U, this.V) == new Vector3(other.L, other.U, other.V); + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double ComputeU(in CieXyz source) diff --git a/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs b/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs index aad7dbf95..fa12b81d2 100644 --- a/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs +++ b/src/ImageSharp/ColorProfiles/CieXyChromaticityCoordinates.cs @@ -3,13 +3,14 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; -// ReSharper disable CompareOfFloatsByEqualityOperator namespace SixLabors.ImageSharp.ColorProfiles; /// /// Represents the coordinates of CIEXY chromaticity space. /// +[StructLayout(LayoutKind.Sequential)] public readonly struct CieXyChromaticityCoordinates : IEquatable { /// @@ -80,5 +81,7 @@ public readonly struct CieXyChromaticityCoordinates : IEquatable [MethodImpl(InliningOptions.ShortMethod)] public bool Equals(CieXyChromaticityCoordinates other) - => new Vector2(this.X, this.Y) == new Vector2(other.X, other.Y); + => this.AsVector2Unsafe() == other.AsVector2Unsafe(); + + private Vector2 AsVector2Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/CieXyy.cs b/src/ImageSharp/ColorProfiles/CieXyy.cs index ea19f5740..62873df14 100644 --- a/src/ImageSharp/ColorProfiles/CieXyy.cs +++ b/src/ImageSharp/ColorProfiles/CieXyy.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -10,6 +11,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// Represents an CIE xyY 1931 color /// /// +[StructLayout(LayoutKind.Sequential)] public readonly struct CieXyy : IColorProfile { /// @@ -150,5 +152,7 @@ public readonly struct CieXyy : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(CieXyy other) - => new Vector3(this.X, this.Y, this.Yl) == new Vector3(other.X, other.Y, other.Yl); + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/CieXyz.cs b/src/ImageSharp/ColorProfiles/CieXyz.cs index fe8755454..07f9b47f9 100644 --- a/src/ImageSharp/ColorProfiles/CieXyz.cs +++ b/src/ImageSharp/ColorProfiles/CieXyz.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -10,6 +11,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// Represents an CIE XYZ 1931 color /// /// +[StructLayout(LayoutKind.Sequential)] public readonly struct CieXyz : IProfileConnectingSpace { /// @@ -86,20 +88,6 @@ public readonly struct CieXyz : IProfileConnectingSpace [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 ToVector3() => new(this.X, this.Y, this.Z); - /// - public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z); - - /// - public override string ToString() => FormattableString.Invariant($"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is CieXyz other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(CieXyz other) - => new Vector3(this.X, this.Y, this.Z) == new Vector3(other.X, other.Y, other.Z); - /// public static CieXyz FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) => new(source.X, source.Y, source.Z); @@ -124,4 +112,20 @@ public readonly struct CieXyz : IProfileConnectingSpace /// public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z); + + /// + public override string ToString() => FormattableString.Invariant($"CieXyz({this.X:#0.##}, {this.Y:#0.##}, {this.Z:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is CieXyz other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(CieXyz other) + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/Cmyk.cs b/src/ImageSharp/ColorProfiles/Cmyk.cs index 88d3249b3..e92490449 100644 --- a/src/ImageSharp/ColorProfiles/Cmyk.cs +++ b/src/ImageSharp/ColorProfiles/Cmyk.cs @@ -3,12 +3,14 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; /// /// Represents an CMYK (cyan, magenta, yellow, keyline) color. /// +[StructLayout(LayoutKind.Sequential)] public readonly struct Cmyk : IColorProfile { private static readonly Vector4 Min = Vector4.Zero; @@ -157,5 +159,7 @@ public readonly struct Cmyk : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Cmyk other) - => new Vector4(this.C, this.M, this.Y, this.K) == new Vector4(other.C, other.M, other.Y, other.K); + => this.AsVector4Unsafe() == other.AsVector4Unsafe(); + + private Vector4 AsVector4Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/Hsl.cs b/src/ImageSharp/ColorProfiles/Hsl.cs index c7ae97c79..2c98c7df9 100644 --- a/src/ImageSharp/ColorProfiles/Hsl.cs +++ b/src/ImageSharp/ColorProfiles/Hsl.cs @@ -3,12 +3,14 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; /// /// Represents a Hsl (hue, saturation, lightness) color. /// +[StructLayout(LayoutKind.Sequential)] public readonly struct Hsl : IColorProfile { private static readonly Vector3 Min = Vector3.Zero; @@ -200,7 +202,9 @@ public readonly struct Hsl : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Hsl other) - => new Vector3(this.H, this.S, this.L) == new Vector3(other.H, other.S, other.L); + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float GetColorComponent(float first, float second, float third) diff --git a/src/ImageSharp/ColorProfiles/Hsv.cs b/src/ImageSharp/ColorProfiles/Hsv.cs index 83445a40f..7535f2463 100644 --- a/src/ImageSharp/ColorProfiles/Hsv.cs +++ b/src/ImageSharp/ColorProfiles/Hsv.cs @@ -3,12 +3,14 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; /// /// Represents a HSV (hue, saturation, value) color. Also known as HSB (hue, saturation, brightness). /// +[StructLayout(LayoutKind.Sequential)] public readonly struct Hsv : IColorProfile { private static readonly Vector3 Min = Vector3.Zero; @@ -223,5 +225,7 @@ public readonly struct Hsv : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Hsv other) - => new Vector3(this.H, this.S, this.V) == new Vector3(other.H, other.S, other.V); + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/HunterLab.cs b/src/ImageSharp/ColorProfiles/HunterLab.cs index eb33e6a95..43ad2ac5c 100644 --- a/src/ImageSharp/ColorProfiles/HunterLab.cs +++ b/src/ImageSharp/ColorProfiles/HunterLab.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -10,6 +11,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// Represents an Hunter LAB color. /// . /// +[StructLayout(LayoutKind.Sequential)] public readonly struct HunterLab : IColorProfile { /// @@ -170,7 +172,9 @@ public readonly struct HunterLab : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(HunterLab other) - => new Vector3(this.L, this.A, this.B) == new Vector3(other.L, other.A, other.B); + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float ComputeKa(in CieXyz whitePoint) diff --git a/src/ImageSharp/ColorProfiles/Lms.cs b/src/ImageSharp/ColorProfiles/Lms.cs index 03a1c5d66..5a6791b2d 100644 --- a/src/ImageSharp/ColorProfiles/Lms.cs +++ b/src/ImageSharp/ColorProfiles/Lms.cs @@ -3,10 +3,17 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; -internal readonly struct Lms : IColorProfile +/// +/// 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. +/// +/// +[StructLayout(LayoutKind.Sequential)] +public readonly struct Lms : IColorProfile { /// /// Initializes a new instance of the struct. @@ -82,20 +89,6 @@ internal readonly struct Lms : IColorProfile [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector3 ToVector3() => new(this.L, this.M, this.S); - /// - public override int GetHashCode() => HashCode.Combine(this.L, this.M, this.S); - - /// - public override string ToString() => FormattableString.Invariant($"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is Lms other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Lms other) - => new Vector3(this.L, this.M, this.S) == new Vector3(other.L, other.M, other.S); - /// public static Lms FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) { @@ -136,4 +129,20 @@ internal readonly struct Lms : IColorProfile /// public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() => ChromaticAdaptionWhitePointSource.WhitePoint; + + /// + public override int GetHashCode() => HashCode.Combine(this.L, this.M, this.S); + + /// + public override string ToString() => FormattableString.Invariant($"Lms({this.L:#0.##}, {this.M:#0.##}, {this.S:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is Lms other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Lms other) + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs index 8036d83e5..664f9d80f 100644 --- a/src/ImageSharp/ColorProfiles/Rgb.cs +++ b/src/ImageSharp/ColorProfiles/Rgb.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; namespace SixLabors.ImageSharp.ColorProfiles; @@ -10,6 +11,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// /// Represents an RGB (red, green, blue) color profile. /// +[StructLayout(LayoutKind.Sequential)] public readonly struct Rgb : IProfileConnectingSpace { /// @@ -79,20 +81,6 @@ public readonly struct Rgb : IProfileConnectingSpace [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rgb left, Rgb right) => !left.Equals(right); - /// - public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); - - /// - public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})"); - - /// - public override bool Equals(object? obj) => obj is Rgb other && this.Equals(other); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Rgb other) - => new Vector3(this.R, this.G, this.B) == new Vector3(other.R, other.G, other.B); - /// public static Rgb FromProfileConnectingSpace(ColorConversionOptions options, in CieXyz source) { @@ -201,6 +189,22 @@ public readonly struct Rgb : IProfileConnectingSpace [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToScaledVector4() => new(this.ToScaledVector3(), 1f); + /// + public override int GetHashCode() => HashCode.Combine(this.R, this.G, this.B); + + /// + public override string ToString() => FormattableString.Invariant($"Rgb({this.R:#0.##}, {this.G:#0.##}, {this.B:#0.##})"); + + /// + public override bool Equals(object? obj) => obj is Rgb other && this.Equals(other); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Rgb other) + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); + private static Matrix4x4 GetCieXyzToRgbMatrix(RgbWorkingSpace workingSpace) { Matrix4x4 matrix = GetRgbToCieXyzMatrix(workingSpace); diff --git a/src/ImageSharp/ColorProfiles/YCbCr.cs b/src/ImageSharp/ColorProfiles/YCbCr.cs index b9f5f65ee..03bd1d312 100644 --- a/src/ImageSharp/ColorProfiles/YCbCr.cs +++ b/src/ImageSharp/ColorProfiles/YCbCr.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.ColorProfiles; @@ -11,6 +12,7 @@ namespace SixLabors.ImageSharp.ColorProfiles; /// /// /// +[StructLayout(LayoutKind.Sequential)] public readonly struct YCbCr : IColorProfile { private static readonly Vector3 Min = Vector3.Zero; @@ -152,5 +154,7 @@ public readonly struct YCbCr : IColorProfile /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(YCbCr other) - => new Vector3(this.Y, this.Cb, this.Cr) == new Vector3(other.Y, other.Cb, other.Cr); + => this.AsVector3Unsafe() == other.AsVector3Unsafe(); + + private Vector3 AsVector3Unsafe() => Unsafe.As(ref Unsafe.AsRef(in this)); } From cceeb2d81ce6b82d4b2d4a640e3ef098dcaf6484 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 22 May 2024 23:10:15 +1000 Subject: [PATCH 45/79] Update Rgb.cs --- src/ImageSharp/ColorProfiles/Rgb.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/ColorProfiles/Rgb.cs b/src/ImageSharp/ColorProfiles/Rgb.cs index 664f9d80f..6698e12cb 100644 --- a/src/ImageSharp/ColorProfiles/Rgb.cs +++ b/src/ImageSharp/ColorProfiles/Rgb.cs @@ -252,15 +252,14 @@ public readonly struct Rgb : IProfileConnectingSpace Vector3 vector = Vector3.Transform(workingSpace.WhitePoint.ToVector3(), inverseXyzMatrix); // Use transposed Rows/Columns - // TODO: Is there a built in method for this multiplication? return new Matrix4x4 { M11 = vector.X * mXr, M21 = vector.Y * mXg, M31 = vector.Z * mXb, - M12 = vector.X * 1, - M22 = vector.Y * 1, - M32 = vector.Z * 1, + M12 = vector.X, + M22 = vector.Y, + M32 = vector.Z, M13 = vector.X * mZr, M23 = vector.Y * mZg, M33 = vector.Z * mZb, From f62e2baac8957b73f0b910d61549087b40f0761a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:00 +0800 Subject: [PATCH 46/79] Make the BMP Decoder Core can skip the file header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make BmpDecoderCore support AlphaMask. link #687 Signed-off-by: 舰队的偶像-岛风酱! --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 226 +++++++++++++++--- .../Formats/Bmp/BmpDecoderOptions.cs | 24 ++ 2 files changed, 211 insertions(+), 39 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index bed489752..9bea6867a 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -71,7 +71,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// /// The file header containing general information. /// - private BmpFileHeader fileHeader; + private BmpFileHeader? fileHeader; /// /// Indicates which bitmap file marker was read. @@ -99,6 +99,15 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// private readonly RleSkippedPixelHandling rleSkippedPixelHandling; + /// + private readonly bool processedAlphaMask; + + /// + private readonly bool skipFileHeader; + + /// + private readonly bool isDoubleHeight; + /// /// Initializes a new instance of the class. /// @@ -109,6 +118,9 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals this.rleSkippedPixelHandling = options.RleSkippedPixelHandling; this.configuration = options.GeneralOptions.Configuration; this.memoryAllocator = this.configuration.MemoryAllocator; + this.processedAlphaMask = options.ProcessedAlphaMask; + this.skipFileHeader = options.SkipFileHeader; + this.isDoubleHeight = options.IsDoubleHeight; } /// @@ -132,38 +144,44 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals switch (this.infoHeader.Compression) { - case BmpCompression.RGB: - if (this.infoHeader.BitsPerPixel == 32) - { - if (this.bmpMetadata.InfoHeaderType == BmpInfoHeaderType.WinVersion3) - { - this.ReadRgb32Slow(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); - } - else - { - this.ReadRgb32Fast(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); - } - } - else if (this.infoHeader.BitsPerPixel == 24) - { - this.ReadRgb24(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); - } - else if (this.infoHeader.BitsPerPixel == 16) - { - this.ReadRgb16(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); - } - else if (this.infoHeader.BitsPerPixel <= 8) - { - this.ReadRgbPalette( - stream, - pixels, - palette, - this.infoHeader.Width, - this.infoHeader.Height, - this.infoHeader.BitsPerPixel, - bytesPerColorMapEntry, - inverted); - } + case BmpCompression.RGB when this.infoHeader.BitsPerPixel is 32 && this.bmpMetadata.InfoHeaderType is BmpInfoHeaderType.WinVersion3: + this.ReadRgb32Slow(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + + break; + case BmpCompression.RGB when this.infoHeader.BitsPerPixel is 32: + this.ReadRgb32Fast(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + + break; + case BmpCompression.RGB when this.infoHeader.BitsPerPixel is 24: + this.ReadRgb24(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + + break; + case BmpCompression.RGB when this.infoHeader.BitsPerPixel is 16: + this.ReadRgb16(stream, pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + + break; + case BmpCompression.RGB when this.infoHeader.BitsPerPixel is <= 8 && this.processedAlphaMask: + this.ReadRgbPaletteWithAlphaMask( + stream, + pixels, + palette, + this.infoHeader.Width, + this.infoHeader.Height, + this.infoHeader.BitsPerPixel, + bytesPerColorMapEntry, + inverted); + + break; + case BmpCompression.RGB when this.infoHeader.BitsPerPixel is <= 8: + this.ReadRgbPalette( + stream, + pixels, + palette, + this.infoHeader.Width, + this.infoHeader.Height, + this.infoHeader.BitsPerPixel, + bytesPerColorMapEntry, + inverted); break; @@ -839,6 +857,108 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals } } + /// + private void ReadRgbPaletteWithAlphaMask(BufferedReadStream stream, Buffer2D pixels, byte[] colors, int width, int height, int bitsPerPixel, int bytesPerColorMapEntry, bool inverted) + where TPixel : unmanaged, IPixel + { + // Pixels per byte (bits per pixel). + int ppb = 8 / bitsPerPixel; + + int arrayWidth = (width + ppb - 1) / ppb; + + // Bit mask + int mask = 0xFF >> (8 - bitsPerPixel); + + // Rows are aligned on 4 byte boundaries. + int padding = arrayWidth % 4; + if (padding != 0) + { + padding = 4 - padding; + } + + Bgra32[,] image = new Bgra32[height, width]; + using (IMemoryOwner row = this.memoryAllocator.Allocate(arrayWidth + padding, AllocationOptions.Clean)) + { + Span rowSpan = row.GetSpan(); + + for (int y = 0; y < height; y++) + { + int newY = Invert(y, height, inverted); + if (stream.Read(rowSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } + + int offset = 0; + + for (int x = 0; x < arrayWidth; x++) + { + int colOffset = x * ppb; + for (int shift = 0, newX = colOffset; shift < ppb && newX < width; shift++, newX++) + { + int colorIndex = ((rowSpan[offset] >> (8 - bitsPerPixel - (shift * bitsPerPixel))) & mask) * bytesPerColorMapEntry; + + image[newY, newX].FromBgr24(Unsafe.As(ref colors[colorIndex])); + } + + offset++; + } + } + } + + arrayWidth = width / 8; + padding = arrayWidth % 4; + if (padding != 0) + { + padding = 4 - padding; + } + + for (int y = 0; y < height; y++) + { + int newY = Invert(y, height, inverted); + + for (int i = 0; i < arrayWidth; i++) + { + int x = i * 8; + int and = stream.ReadByte(); + if (and is -1) + { + throw new EndOfStreamException(); + } + + for (int j = 0; j < 8; j++) + { + SetAlpha(ref image[newY, x + j], and, j); + } + } + + stream.Skip(padding); + } + + for (int y = 0; y < height; y++) + { + int newY = Invert(y, height, inverted); + Span pixelRow = pixels.DangerousGetRowSpan(newY); + + for (int x = 0; x < width; x++) + { + pixelRow[x].FromBgra32(image[newY, x]); + } + } + } + + /// + /// Set pixel's alpha with alpha mask. + /// + /// Bgra32 pixel. + /// alpha mask. + /// bit index of pixel. + private static void SetAlpha(ref Bgra32 pixel, in int mask, in int index) + { + bool isTransparently = (mask & (0b10000000 >> index)) is not 0; + pixel.A = isTransparently ? byte.MinValue : byte.MaxValue; + } + /// /// Reads the 16 bit color palette from the stream. /// @@ -1333,6 +1453,11 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals this.metadata.VerticalResolution = Math.Round(UnitConverter.InchToMeter(ImageMetadata.DefaultVerticalResolution)); } + if (this.isDoubleHeight) + { + this.infoHeader.Height >>= 1; + } + ushort bitsPerPixel = this.infoHeader.BitsPerPixel; this.bmpMetadata = this.metadata.GetBmpMetadata(); this.bmpMetadata.InfoHeaderType = infoHeaderType; @@ -1362,9 +1487,9 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals // The bitmap file header of the first image follows the array header. stream.Read(buffer, 0, BmpFileHeader.Size); this.fileHeader = BmpFileHeader.Parse(buffer); - if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap) + if (this.fileHeader.Value.Type != BmpConstants.TypeMarkers.Bitmap) { - BmpThrowHelper.ThrowNotSupportedException($"Unsupported bitmap file inside a BitmapArray file. File header bitmap type marker '{this.fileHeader.Type}'."); + BmpThrowHelper.ThrowNotSupportedException($"Unsupported bitmap file inside a BitmapArray file. File header bitmap type marker '{this.fileHeader.Value.Type}'."); } break; @@ -1387,7 +1512,11 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals [MemberNotNull(nameof(bmpMetadata))] private int ReadImageHeaders(BufferedReadStream stream, out bool inverted, out byte[] palette) { - this.ReadFileHeader(stream); + if (!this.skipFileHeader) + { + this.ReadFileHeader(stream); + } + this.ReadInfoHeader(stream); // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517 @@ -1411,7 +1540,21 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals switch (this.fileMarkerType) { case BmpFileMarkerType.Bitmap: - colorMapSizeBytes = this.fileHeader.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize; + if (this.fileHeader.HasValue) + { + colorMapSizeBytes = this.fileHeader.Value.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize; + } + else + { + colorMapSizeBytes = this.infoHeader.ClrUsed; + if (colorMapSizeBytes is 0 && this.infoHeader.BitsPerPixel is <= 8) + { + colorMapSizeBytes = ColorNumerics.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel); + } + + colorMapSizeBytes *= 4; + } + int colorCountForBitDepth = ColorNumerics.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel); bytesPerColorMapEntry = colorMapSizeBytes / colorCountForBitDepth; @@ -1442,7 +1585,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals { // Usually the color palette is 1024 byte (256 colors * 4), but the documentation does not mention a size limit. // Make sure, that we will not read pass the bitmap offset (starting position of image data). - if (stream.Position > this.fileHeader.Offset - colorMapSizeBytes) + if (this.fileHeader.HasValue && stream.Position > this.fileHeader.Value.Offset - colorMapSizeBytes) { BmpThrowHelper.ThrowInvalidImageContentException( $"Reading the color map would read beyond the bitmap offset. Either the color map size of '{colorMapSizeBytes}' is invalid or the bitmap offset."); @@ -1456,7 +1599,12 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals } } - int skipAmount = this.fileHeader.Offset - (int)stream.Position; + int skipAmount = 0; + if (this.fileHeader.HasValue) + { + skipAmount = this.fileHeader.Value.Offset - (int)stream.Position; + } + if ((skipAmount + (int)stream.Position) > stream.Length) { BmpThrowHelper.ThrowInvalidImageContentException("Invalid file header offset found. Offset is greater than the stream length."); diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs index b3387ce80..17d37cab2 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs @@ -16,4 +16,28 @@ public sealed class BmpDecoderOptions : ISpecializedDecoderOptions /// which can occur during decoding run length encoded bitmaps. /// public RleSkippedPixelHandling RleSkippedPixelHandling { get; init; } + + /// + /// Gets a value indicating whether the additional AlphaMask is processed at decoding time. + /// + /// + /// It will be used at IcoDecoder. + /// + internal bool ProcessedAlphaMask { get; init; } + + /// + /// Gets a value indicating whether to skip loading the BMP file header. + /// + /// + /// It will be used at IcoDecoder. + /// + internal bool SkipFileHeader { get; init; } + + /// + /// Gets a value indicating whether the height is double of true height. + /// + /// + /// It will be used at IcoDecoder. + /// + internal bool IsDoubleHeight { get; init; } } From 520ff46baaf9927a734947d167072a40bd5b85a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:02 +0800 Subject: [PATCH 47/79] Append BMP's MIME Type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- src/ImageSharp/Formats/Bmp/BmpConstants.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpConstants.cs b/src/ImageSharp/Formats/Bmp/BmpConstants.cs index 5cf0c9732..62edfdfdf 100644 --- a/src/ImageSharp/Formats/Bmp/BmpConstants.cs +++ b/src/ImageSharp/Formats/Bmp/BmpConstants.cs @@ -11,7 +11,12 @@ internal static class BmpConstants /// /// The list of mimetypes that equate to a bmp. /// - public static readonly IEnumerable MimeTypes = new[] { "image/bmp", "image/x-windows-bmp" }; + public static readonly IEnumerable MimeTypes = new[] + { + "image/bmp", + "image/x-windows-bmp", + "image/x-win-bitmap" + }; /// /// The list of file extensions that equate to a bmp. From 11293824e3213133eb6e1e4bcdec8a67fed3cbbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:03 +0800 Subject: [PATCH 48/79] Add UnsafeSetFormatMetadata in ImageFrameMetadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- src/ImageSharp/Metadata/ImageFrameMetadata.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ImageSharp/Metadata/ImageFrameMetadata.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs index 1c0330d5d..0567c8916 100644 --- a/src/ImageSharp/Metadata/ImageFrameMetadata.cs +++ b/src/ImageSharp/Metadata/ImageFrameMetadata.cs @@ -99,6 +99,11 @@ public sealed class ImageFrameMetadata : IDeepCloneable return newMeta; } + internal void UnsafeSetFormatMetadata( + IImageFormat key, + IDeepCloneable value) + => this.formatMetadata[key] = value; + /// /// Gets the metadata value associated with the specified key. /// From 4832d8f24aaadef2827d46f988be0a77bca5018a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:03 +0800 Subject: [PATCH 49/79] Add Icon Support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ico Decoder - Ico Detector - Ico Detector UnitTest - Cur Decoder - Cur Detector - Cur Detector UnitTest Signed-off-by: 舰队的偶像-岛风酱! --- ImageSharp.sln | 7 + src/ImageSharp/Configuration.cs | 6 +- .../Icon/Cur/CurConfigurationModule.cs | 19 +++ .../Formats/Icon/Cur/CurConstants.cs | 28 ++++ src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs | 47 ++++++ .../Formats/Icon/Cur/CurDecoderCore.cs | 16 +++ src/ImageSharp/Formats/Icon/Cur/CurFormat.cs | 37 +++++ .../Formats/Icon/Cur/CurFrameMetadata.cs | 52 +++++++ .../Formats/Icon/Cur/CurMetadata.cs | 16 +++ .../Formats/Icon/Cur/MetadataExtensions.cs | 44 ++++++ .../Icon/Ico/IcoConfigurationModule.cs | 19 +++ .../Formats/Icon/Ico/IcoConstants.cs | 38 +++++ src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs | 47 ++++++ .../Formats/Icon/Ico/IcoDecoderCore.cs | 16 +++ src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs | 37 +++++ .../Formats/Icon/Ico/IcoFrameMetadata.cs | 50 +++++++ .../Formats/Icon/Ico/IcoMetadata.cs | 16 +++ .../Formats/Icon/Ico/MetadataExtensions.cs | 44 ++++++ src/ImageSharp/Formats/Icon/IconAssert.cs | 43 ++++++ .../Formats/Icon/IconDecoderCore.cs | 136 ++++++++++++++++++ src/ImageSharp/Formats/Icon/IconDir.cs | 26 ++++ src/ImageSharp/Formats/Icon/IconDirEntry.cs | 28 ++++ src/ImageSharp/Formats/Icon/IconFileType.cs | 20 +++ .../Formats/Icon/IconFrameCompression.cs | 25 ++++ .../Formats/Icon/IconFrameMetadata.cs | 103 +++++++++++++ .../Formats/Icon/IconImageFormatDetector.cs | 33 +++++ tests/ImageSharp.Tests/ConfigurationTests.cs | 2 +- .../Formats/Icon/Cur/CurDecoderTests.cs | 23 +++ .../Formats/Icon/Ico/IcoDecoderTests.cs | 23 +++ tests/ImageSharp.Tests/TestImages.cs | 10 ++ tests/Images/Input/Icon/aero_arrow.cur | 3 + tests/Images/Input/Icon/flutter.ico | 3 + 32 files changed, 1015 insertions(+), 2 deletions(-) create mode 100644 src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs create mode 100644 src/ImageSharp/Formats/Icon/Cur/CurConstants.cs create mode 100644 src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs create mode 100644 src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs create mode 100644 src/ImageSharp/Formats/Icon/Cur/CurFormat.cs create mode 100644 src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs create mode 100644 src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs create mode 100644 src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/IcoMetadata.cs create mode 100644 src/ImageSharp/Formats/Icon/Ico/MetadataExtensions.cs create mode 100644 src/ImageSharp/Formats/Icon/IconAssert.cs create mode 100644 src/ImageSharp/Formats/Icon/IconDecoderCore.cs create mode 100644 src/ImageSharp/Formats/Icon/IconDir.cs create mode 100644 src/ImageSharp/Formats/Icon/IconDirEntry.cs create mode 100644 src/ImageSharp/Formats/Icon/IconFileType.cs create mode 100644 src/ImageSharp/Formats/Icon/IconFrameCompression.cs create mode 100644 src/ImageSharp/Formats/Icon/IconFrameMetadata.cs create mode 100644 src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs create mode 100644 tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs create mode 100644 tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs create mode 100644 tests/Images/Input/Icon/aero_arrow.cur create mode 100644 tests/Images/Input/Icon/flutter.ico diff --git a/ImageSharp.sln b/ImageSharp.sln index 162de8416..7ccd92c07 100644 --- a/ImageSharp.sln +++ b/ImageSharp.sln @@ -661,6 +661,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Qoi", "Qoi", "{E801B508-493 tests\Images\Input\Qoi\wikipedia_008.qoi = tests\Images\Input\Qoi\wikipedia_008.qoi EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Icon", "Icon", "{95E45DDE-A67D-48AD-BBA8-5FAA151B860D}" + ProjectSection(SolutionItems) = preProject + tests\Images\Input\Icon\aero_arrow.cur = tests\Images\Input\Icon\aero_arrow.cur + tests\Images\Input\Icon\flutter.ico = tests\Images\Input\Icon\flutter.ico + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -714,6 +720,7 @@ Global {670DD46C-82E9-499A-B2D2-00A802ED0141} = {E1C42A6F-913B-4A7B-B1A8-2BB62843B254} {5DFC394F-136F-4B76-9BCA-3BA786515EFC} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66} {E801B508-4935-41CD-BA85-CF11BFF55A45} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66} + {95E45DDE-A67D-48AD-BBA8-5FAA151B860D} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795} diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index 1ca5d0a46..d6cfd480d 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -5,6 +5,8 @@ using System.Collections.Concurrent; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Icon.Cur; +using SixLabors.ImageSharp.Formats.Icon.Ico; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Pbm; using SixLabors.ImageSharp.Formats.Png; @@ -222,5 +224,7 @@ public sealed class Configuration new TgaConfigurationModule(), new TiffConfigurationModule(), new WebpConfigurationModule(), - new QoiConfigurationModule()); + new QoiConfigurationModule(), + new IcoConfigurationModule(), + new CurConfigurationModule()); } diff --git a/src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs b/src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs new file mode 100644 index 000000000..c975bc609 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +/// +/// Registers the image encoders, decoders and mime type detectors for the Ico format. +/// +public sealed class CurConfigurationModule : IImageFormatConfigurationModule +{ + /// + public void Configure(Configuration configuration) + { + // TODO: CurEncoder + // configuration.ImageFormatsManager.SetEncoder(CurFormat.Instance, new CurEncoder()); + configuration.ImageFormatsManager.SetDecoder(CurFormat.Instance, CurDecoder.Instance); + configuration.ImageFormatsManager.AddImageFormatDetector(new IconImageFormatDetector()); + } +} diff --git a/src/ImageSharp/Formats/Icon/Cur/CurConstants.cs b/src/ImageSharp/Formats/Icon/Cur/CurConstants.cs new file mode 100644 index 000000000..701b40cf4 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/CurConstants.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +/// +/// Defines constants relating to ICOs +/// +internal static class CurConstants +{ + /// + /// The list of mimetypes that equate to a ico. + /// + /// + /// See + /// + public static readonly IEnumerable MimeTypes = new[] + { + "application/octet-stream", + }; + + /// + /// The list of file extensions that equate to a ico. + /// + public static readonly IEnumerable FileExtensions = new[] { "cur" }; + + public const uint FileHeader = 0x00_02_00_00; +} diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs b/src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs new file mode 100644 index 000000000..ceefdcaba --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +/// +/// Decoder for generating an image out of a ico encoded stream. +/// +public sealed class CurDecoder : ImageDecoder +{ + private CurDecoder() + { + } + + /// + /// Gets the shared instance. + /// + public static CurDecoder Instance { get; } = new(); + + /// + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + + Image image = new CurDecoderCore(options).Decode(options.Configuration, stream, cancellationToken); + + ScaleToTargetSize(options, image); + + return image; + } + + /// + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); + + /// + protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + + return new CurDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs new file mode 100644 index 000000000..8b08f127d --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Metadata; + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +internal sealed class CurDecoderCore : IconDecoderCore +{ + public CurDecoderCore(DecoderOptions options) + : base(options) + { + } + + protected override IconFrameMetadata GetFrameMetadata(ImageFrameMetadata metadata) => metadata.GetCurMetadata(); +} diff --git a/src/ImageSharp/Formats/Icon/Cur/CurFormat.cs b/src/ImageSharp/Formats/Icon/Cur/CurFormat.cs new file mode 100644 index 000000000..1e5758bc4 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/CurFormat.cs @@ -0,0 +1,37 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +/// +/// Registers the image encoders, decoders and mime type detectors for the ICO format. +/// +public sealed class CurFormat : IImageFormat +{ + private CurFormat() + { + } + + /// + /// Gets the shared instance. + /// + public static CurFormat Instance { get; } = new(); + + /// + public string Name => "ICO"; + + /// + public string DefaultMimeType => CurConstants.MimeTypes.First(); + + /// + public IEnumerable MimeTypes => CurConstants.MimeTypes; + + /// + public IEnumerable FileExtensions => CurConstants.FileExtensions; + + /// + public CurMetadata CreateDefaultFormatMetadata() => new(); + + /// + public CurFrameMetadata CreateDefaultFormatFrameMetadata() => new(); +} diff --git a/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs b/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs new file mode 100644 index 000000000..c94afdd3a --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs @@ -0,0 +1,52 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +/// +/// IcoFrameMetadata +/// +public class CurFrameMetadata : IconFrameMetadata, IDeepCloneable, IDeepCloneable +{ + /// + /// Initializes a new instance of the class. + /// + public CurFrameMetadata() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// metadata + public CurFrameMetadata(IconFrameMetadata metadata) + : base(metadata) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// width + /// height + /// colorCount + /// field1 + /// field2 + public CurFrameMetadata(byte width, byte height, byte colorCount, ushort field1, ushort field2) + : base(width, height, colorCount, field1, field2) + { + } + + /// + /// Gets or sets Specifies the horizontal coordinates of the hotspot in number of pixels from the left. + /// + public ushort HotspotX { get => this.Field1; set => this.Field1 = value; } + + /// + /// Gets or sets Specifies the vertical coordinates of the hotspot in number of pixels from the top. + /// + public ushort HotspotY { get => this.Field2; set => this.Field2 = value; } + + /// + public override CurFrameMetadata DeepClone() => new(this); +} diff --git a/src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs b/src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs new file mode 100644 index 000000000..ed3c322b4 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +/// +/// Provides Ico specific metadata information for the image. +/// +public class CurMetadata : IDeepCloneable, IDeepCloneable +{ + /// + public CurMetadata DeepClone() => new(); + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); +} diff --git a/src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs b/src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs new file mode 100644 index 000000000..400ece648 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Metadata; + +namespace SixLabors.ImageSharp.Formats.Icon.Cur; + +/// +/// Extension methods for the type. +/// +public static class MetadataExtensions +{ + /// + /// Gets the Icon format specific metadata for the image. + /// + /// The metadata this method extends. + /// The . + public static CurMetadata GetCurMetadata(this ImageMetadata source) + => source.GetFormatMetadata(CurFormat.Instance); + + /// + /// Gets the Icon format specific metadata for the image frame. + /// + /// The metadata this method extends. + /// The . + public static CurFrameMetadata GetCurMetadata(this ImageFrameMetadata source) + => source.GetFormatMetadata(CurFormat.Instance); + + /// + /// Gets the Icon format specific metadata for the image frame. + /// + /// The metadata this method extends. + /// + /// When this method returns, contains the metadata associated with the specified frame, + /// if found; otherwise, the default value for the type of the metadata parameter. + /// This parameter is passed uninitialized. + /// + /// + /// if the Icon frame metadata exists; otherwise, . + /// + public static bool TryGetCurMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out CurFrameMetadata? metadata) + => source.TryGetFormatMetadata(CurFormat.Instance, out metadata); +} diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs b/src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs new file mode 100644 index 000000000..7074189c7 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +/// +/// Registers the image encoders, decoders and mime type detectors for the Ico format. +/// +public sealed class IcoConfigurationModule : IImageFormatConfigurationModule +{ + /// + public void Configure(Configuration configuration) + { + // TODO: IcoEncoder + // configuration.ImageFormatsManager.SetEncoder(IcoFormat.Instance, new IcoEncoder()); + configuration.ImageFormatsManager.SetDecoder(IcoFormat.Instance, IcoDecoder.Instance); + configuration.ImageFormatsManager.AddImageFormatDetector(new IconImageFormatDetector()); + } +} diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs b/src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs new file mode 100644 index 000000000..345711705 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs @@ -0,0 +1,38 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +/// +/// Defines constants relating to ICOs +/// +internal static class IcoConstants +{ + /// + /// The list of mimetypes that equate to a ico. + /// + /// + /// See + /// + public static readonly IEnumerable MimeTypes = new[] + { + // IANA-registered + "image/vnd.microsoft.icon", + + // ICO & CUR types used by Windows + "image/x-icon", + + // Erroneous types but have been used + "image/ico", + "image/icon", + "text/ico", + "application/ico", + }; + + /// + /// The list of file extensions that equate to a ico. + /// + public static readonly IEnumerable FileExtensions = new[] { "ico" }; + + public const uint FileHeader = 0x00_01_00_00; +} diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs b/src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs new file mode 100644 index 000000000..5d6137920 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +/// +/// Decoder for generating an image out of a ico encoded stream. +/// +public sealed class IcoDecoder : ImageDecoder +{ + private IcoDecoder() + { + } + + /// + /// Gets the shared instance. + /// + public static IcoDecoder Instance { get; } = new(); + + /// + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + + Image image = new IcoDecoderCore(options).Decode(options.Configuration, stream, cancellationToken); + + ScaleToTargetSize(options, image); + + return image; + } + + /// + protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); + + /// + protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + { + Guard.NotNull(options, nameof(options)); + Guard.NotNull(stream, nameof(stream)); + + return new IcoDecoderCore(options).Identify(options.Configuration, stream, cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs new file mode 100644 index 000000000..0782c2128 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Metadata; + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +internal sealed class IcoDecoderCore : IconDecoderCore +{ + public IcoDecoderCore(DecoderOptions options) + : base(options) + { + } + + protected override IconFrameMetadata GetFrameMetadata(ImageFrameMetadata metadata) => metadata.GetIcoMetadata(); +} diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs b/src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs new file mode 100644 index 000000000..27b0525bf --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs @@ -0,0 +1,37 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +/// +/// Registers the image encoders, decoders and mime type detectors for the ICO format. +/// +public sealed class IcoFormat : IImageFormat +{ + private IcoFormat() + { + } + + /// + /// Gets the shared instance. + /// + public static IcoFormat Instance { get; } = new(); + + /// + public string Name => "ICO"; + + /// + public string DefaultMimeType => IcoConstants.MimeTypes.First(); + + /// + public IEnumerable MimeTypes => IcoConstants.MimeTypes; + + /// + public IEnumerable FileExtensions => IcoConstants.FileExtensions; + + /// + public IcoMetadata CreateDefaultFormatMetadata() => new(); + + /// + public IcoFrameMetadata CreateDefaultFormatFrameMetadata() => new(); +} diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs new file mode 100644 index 000000000..7c903facd --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs @@ -0,0 +1,50 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +/// +/// IcoFrameMetadata +/// +public class IcoFrameMetadata : IconFrameMetadata, IDeepCloneable, IDeepCloneable +{ + /// + /// Initializes a new instance of the class. + /// + public IcoFrameMetadata() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// metadata + public IcoFrameMetadata(IconFrameMetadata metadata) + : base(metadata) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// width + /// height + /// colorCount + /// field1 + /// field2 + public IcoFrameMetadata(byte width, byte height, byte colorCount, ushort field1, ushort field2) + : base(width, height, colorCount, field1, field2) + { + } + + /// + /// Gets or sets Specifies bits per pixel. + /// + /// + /// It may used by Encoder. + /// + public ushort BitCount { get => this.Field2; set => this.Field2 = value; } + + /// + public override IcoFrameMetadata DeepClone() => new(this); +} diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoMetadata.cs b/src/ImageSharp/Formats/Icon/Ico/IcoMetadata.cs new file mode 100644 index 000000000..b227d0bd6 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/IcoMetadata.cs @@ -0,0 +1,16 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +/// +/// Provides Ico specific metadata information for the image. +/// +public class IcoMetadata : IDeepCloneable, IDeepCloneable +{ + /// + public IcoMetadata DeepClone() => new(); + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); +} diff --git a/src/ImageSharp/Formats/Icon/Ico/MetadataExtensions.cs b/src/ImageSharp/Formats/Icon/Ico/MetadataExtensions.cs new file mode 100644 index 000000000..fb5b4afe7 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/Ico/MetadataExtensions.cs @@ -0,0 +1,44 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Metadata; + +namespace SixLabors.ImageSharp.Formats.Icon.Ico; + +/// +/// Extension methods for the type. +/// +public static class MetadataExtensions +{ + /// + /// Gets the Ico format specific metadata for the image. + /// + /// The metadata this method extends. + /// The . + public static IcoMetadata GetIcoMetadata(this ImageMetadata source) + => source.GetFormatMetadata(IcoFormat.Instance); + + /// + /// Gets the Ico format specific metadata for the image frame. + /// + /// The metadata this method extends. + /// The . + public static IcoFrameMetadata GetIcoMetadata(this ImageFrameMetadata source) + => source.GetFormatMetadata(IcoFormat.Instance); + + /// + /// Gets the Ico format specific metadata for the image frame. + /// + /// The metadata this method extends. + /// + /// When this method returns, contains the metadata associated with the specified frame, + /// if found; otherwise, the default value for the type of the metadata parameter. + /// This parameter is passed uninitialized. + /// + /// + /// if the Ico frame metadata exists; otherwise, . + /// + public static bool TryGetIcoMetadata(this ImageFrameMetadata source, [NotNullWhen(true)] out IcoFrameMetadata? metadata) + => source.TryGetFormatMetadata(IcoFormat.Instance, out metadata); +} diff --git a/src/ImageSharp/Formats/Icon/IconAssert.cs b/src/ImageSharp/Formats/Icon/IconAssert.cs new file mode 100644 index 000000000..bcb427c1a --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconAssert.cs @@ -0,0 +1,43 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon; + +internal class IconAssert +{ + internal static void CanSeek(Stream stream) + { + if (!stream.CanSeek) + { + throw new NotSupportedException("This stream cannot support seek"); + } + } + + internal static int EndOfStream(int v, int length) + { + if (v != length) + { + throw new EndOfStreamException(); + } + + return v; + } + + internal static long EndOfStream(long v, long length) + { + if (v != length) + { + throw new EndOfStreamException(); + } + + return v; + } + + internal static void NotSquare(in Size size) + { + if (size.Width != size.Height) + { + throw new FormatException("This image is not square."); + } + } +} diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs new file mode 100644 index 000000000..d9a578ff2 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -0,0 +1,136 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.InteropServices; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.IO; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Icon; + +internal abstract class IconDecoderCore : IImageDecoderInternals +{ + private IconDir fileHeader; + + public IconDecoderCore(DecoderOptions options) => this.Options = options; + + public DecoderOptions Options { get; } + + public Size Dimensions { get; private set; } + + protected IconDir FileHeader { get => this.fileHeader; private set => this.fileHeader = value; } + + protected IconDirEntry[] Entries { get; private set; } = Array.Empty(); + + public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel + { + // Stream may not at 0. + long basePosition = stream.Position; + this.ReadHeader(stream); + + Span flag = stackalloc byte[Png.PngConstants.HeaderBytes.Length]; + + List<(Image Image, bool IsPng, int Index)> frames = new(this.Entries.Length); + for (int i = 0; i < this.Entries.Length; i++) + { + _ = IconAssert.EndOfStream(stream.Seek(basePosition + this.Entries[i].ImageOffset, SeekOrigin.Begin), basePosition + this.Entries[i].ImageOffset); + _ = IconAssert.EndOfStream(stream.Read(flag), Png.PngConstants.HeaderBytes.Length); + _ = stream.Seek(-Png.PngConstants.HeaderBytes.Length, SeekOrigin.Current); + + bool isPng = flag.SequenceEqual(Png.PngConstants.HeaderBytes); + + Image img = this.GetDecoder(isPng).Decode(stream, cancellationToken); + IconAssert.NotSquare(img.Size); + frames.Add((img, isPng, i)); + if (isPng && img.Size.Width > this.Dimensions.Width) + { + this.Dimensions = img.Size; + } + } + + ImageMetadata metadata = new(); + return new(this.Options.Configuration, metadata, frames.Select(i => + { + ImageFrame target = new(this.Options.Configuration, this.Dimensions); + ImageFrame source = i.Image.Frames.RootFrameUnsafe; + for (int h = 0; h < source.Height; h++) + { + source.PixelBuffer.DangerousGetRowSpan(h).CopyTo(target.PixelBuffer.DangerousGetRowSpan(h)); + } + + if (i.IsPng) + { + target.Metadata.UnsafeSetFormatMetadata(Png.PngFormat.Instance, i.Image.Metadata.GetPngMetadata()); + } + else + { + target.Metadata.UnsafeSetFormatMetadata(Bmp.BmpFormat.Instance, i.Image.Metadata.GetBmpMetadata()); + } + + this.GetFrameMetadata(target.Metadata).FromIconDirEntry(this.Entries[i.Index]); + + i.Image.Dispose(); + return target; + }).ToArray()); + } + + public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) + { + this.ReadHeader(stream); + + ImageMetadata metadata = new(); + ImageFrameMetadata[] frames = new ImageFrameMetadata[this.FileHeader.Count]; + for (int i = 0; i < frames.Length; i++) + { + frames[i] = new(); + IconFrameMetadata icoFrameMetadata = this.GetFrameMetadata(frames[i]); + icoFrameMetadata.FromIconDirEntry(this.Entries[i]); + } + + return new(new(32), new(0), metadata, frames); + } + + protected abstract IconFrameMetadata GetFrameMetadata(ImageFrameMetadata metadata); + + protected void ReadHeader(Stream stream) + { + _ = Read(stream, out this.fileHeader, IconDir.Size); + this.Entries = new IconDirEntry[this.FileHeader.Count]; + for (int i = 0; i < this.Entries.Length; i++) + { + _ = Read(stream, out this.Entries[i], IconDirEntry.Size); + } + + this.Dimensions = new( + this.Entries.Max(i => i.Width), + this.Entries.Max(i => i.Height)); + } + + private static int Read(Stream stream, out T data, int size) + where T : unmanaged + { + Span buffer = stackalloc byte[size]; + _ = IconAssert.EndOfStream(stream.Read(buffer), size); + data = MemoryMarshal.Cast(buffer)[0]; + return size; + } + + private IImageDecoderInternals GetDecoder(bool isPng) + { + if (isPng) + { + return new Png.PngDecoderCore(this.Options); + } + else + { + return new Bmp.BmpDecoderCore(new() + { + ProcessedAlphaMask = true, + SkipFileHeader = true, + IsDoubleHeight = true, + }); + } + } +} diff --git a/src/ImageSharp/Formats/Icon/IconDir.cs b/src/ImageSharp/Formats/Icon/IconDir.cs new file mode 100644 index 000000000..f1281a568 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconDir.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Formats.Icon; + +[StructLayout(LayoutKind.Sequential, Pack = 1, Size = Size)] +internal struct IconDir +{ + public const int Size = 3 * sizeof(ushort); + public ushort Reserved; + public IconFileType Type; + public ushort Count; + + public IconDir(IconFileType type) + : this(type, 0) + => this.Type = type; + + public IconDir(IconFileType type, ushort count) + { + this.Reserved = 0; + this.Type = type; + this.Count = count; + } +} diff --git a/src/ImageSharp/Formats/Icon/IconDirEntry.cs b/src/ImageSharp/Formats/Icon/IconDirEntry.cs new file mode 100644 index 000000000..43254f89d --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconDirEntry.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Formats.Icon; + +[StructLayout(LayoutKind.Sequential, Pack = 1, Size = Size)] +internal struct IconDirEntry +{ + public const int Size = (4 * sizeof(byte)) + (2 * sizeof(ushort)) + (2 * sizeof(uint)); + + public byte Width; + + public byte Height; + + public byte ColorCount; + + public byte Reserved; + + public ushort Planes; + + public ushort BitCount; + + public uint BytesInRes; + + public uint ImageOffset; +} diff --git a/src/ImageSharp/Formats/Icon/IconFileType.cs b/src/ImageSharp/Formats/Icon/IconFileType.cs new file mode 100644 index 000000000..3450698f1 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconFileType.cs @@ -0,0 +1,20 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon; + +/// +/// Ico file type +/// +internal enum IconFileType : ushort +{ + /// + /// ICO file + /// + ICO = 1, + + /// + /// CUR file + /// + CUR = 2, +} diff --git a/src/ImageSharp/Formats/Icon/IconFrameCompression.cs b/src/ImageSharp/Formats/Icon/IconFrameCompression.cs new file mode 100644 index 000000000..f6509f40c --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconFrameCompression.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon; + +/// +/// IconFrameCompression +/// +public enum IconFrameCompression +{ + /// + /// Unknown + /// + Unknown, + + /// + /// Bmp + /// + Bmp, + + /// + /// Png + /// + Png +} diff --git a/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs b/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs new file mode 100644 index 000000000..17e641c71 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs @@ -0,0 +1,103 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Icon; + +/// +/// IconFrameMetadata +/// +public abstract class IconFrameMetadata : IDeepCloneable +{ + /// + /// Initializes a new instance of the class. + /// + protected IconFrameMetadata() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// width + /// height + /// colorCount + /// field1 + /// field2 + protected IconFrameMetadata(byte width, byte height, byte colorCount, ushort field1, ushort field2) + { + this.EncodingWidth = width; + this.EncodingHeight = height; + this.ColorCount = colorCount; + this.Field1 = field1; + this.Field2 = field2; + } + + /// + protected IconFrameMetadata(IconFrameMetadata metadata) + { + this.EncodingWidth = metadata.EncodingWidth; + this.EncodingHeight = metadata.EncodingHeight; + this.ColorCount = metadata.ColorCount; + this.Field1 = metadata.Field1; + this.Field2 = metadata.Field2; + } + + /// + /// Gets or sets icoFrameCompression. + /// + public IconFrameCompression Compression { get; protected set; } + + /// + /// Gets or sets ColorCount field.
+ /// Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette. + ///
+ // TODO: BmpMetadata does not supported palette yet. + public byte ColorCount { get; set; } + + /// + /// Gets or sets Planes field.
+ /// In ICO format: Specifies color planes. Should be 0 or 1.
+ /// In CUR format: Specifies the horizontal coordinates of the hotspot in number of pixels from the left. + ///
+ protected ushort Field1 { get; set; } + + /// + /// Gets or sets BitCount field.
+ /// In ICO format: Specifies bits per pixel.
+ /// In CUR format: Specifies the vertical coordinates of the hotspot in number of pixels from the top. + ///
+ protected ushort Field2 { get; set; } + + /// + /// Gets or sets Height field.
+ /// Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels. + ///
+ public byte EncodingHeight { get; set; } + + /// + /// Gets or sets Width field.
+ /// Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels. + ///
+ public byte EncodingWidth { get; set; } + + /// + public abstract IDeepCloneable DeepClone(); + + internal void FromIconDirEntry(in IconDirEntry metadata) + { + this.EncodingWidth = metadata.Width; + this.EncodingHeight = metadata.Height; + this.ColorCount = metadata.ColorCount; + this.Field1 = metadata.Planes; + this.Field2 = metadata.BitCount; + } + + internal IconDirEntry ToIconDirEntry() => new() + { + Width = this.EncodingWidth, + Height = this.EncodingHeight, + ColorCount = this.ColorCount, + Planes = this.Field1, + BitCount = this.Field2, + }; +} diff --git a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs new file mode 100644 index 000000000..aff8dfe0a --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs @@ -0,0 +1,33 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp.Formats.Icon; + +/// +/// Detects ico file headers. +/// +public class IconImageFormatDetector : IImageFormatDetector +{ + /// + public int HeaderSize { get; } = 4; + + /// + public bool TryDetectFormat(ReadOnlySpan header, [NotNullWhen(true)] out IImageFormat? format) + { + switch (MemoryMarshal.Cast(header)[0]) + { + case Ico.IcoConstants.FileHeader: + format = Ico.IcoFormat.Instance; + return true; + case Cur.CurConstants.FileHeader: + format = Cur.CurFormat.Instance; + return true; + default: + format = default; + return false; + } + } +} diff --git a/tests/ImageSharp.Tests/ConfigurationTests.cs b/tests/ImageSharp.Tests/ConfigurationTests.cs index c5d61726c..c8e6cd265 100644 --- a/tests/ImageSharp.Tests/ConfigurationTests.cs +++ b/tests/ImageSharp.Tests/ConfigurationTests.cs @@ -20,7 +20,7 @@ public class ConfigurationTests public Configuration DefaultConfiguration { get; } - private readonly int expectedDefaultConfigurationCount = 9; + private readonly int expectedDefaultConfigurationCount = 11; public ConfigurationTests() { diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs new file mode 100644 index 000000000..c3c9ad1c4 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Icon.Cur; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.PixelFormats; +using static SixLabors.ImageSharp.Tests.TestImages.Cur; + +namespace SixLabors.ImageSharp.Tests.Formats.Icon.Cur; + +[Trait("Format", "Cur")] +[ValidateDisposedMemoryAllocations] +public class CurDecoderTests +{ + [Theory] + [WithFile(WindowsMouse, PixelTypes.Rgba32)] + public void CurDecoder_Decode(TestImageProvider provider) + { + using Image image = provider.GetImage(CurDecoder.Instance); + + image.DebugSave(provider, extension: "tiff", encoder: new TiffEncoder()); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs new file mode 100644 index 000000000..6e7b35111 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -0,0 +1,23 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Icon.Ico; +using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.PixelFormats; +using static SixLabors.ImageSharp.Tests.TestImages.Ico; + +namespace SixLabors.ImageSharp.Tests.Formats.Icon.Ico; + +[Trait("Format", "Icon")] +[ValidateDisposedMemoryAllocations] +public class IcoDecoderTests +{ + [Theory] + [WithFile(Flutter, PixelTypes.Rgba32)] + public void IcoDecoder_Decode(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSave(provider, extension: "tiff", encoder: new TiffEncoder()); + } +} diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index a45931e29..c64bf2b34 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1121,4 +1121,14 @@ public static class TestImages public const string TestCardRGBA = "Qoi/testcard_rgba.qoi"; public const string Wikipedia008 = "Qoi/wikipedia_008.qoi"; } + + public static class Ico + { + public const string Flutter = "Icon/flutter.ico"; + } + + public static class Cur + { + public const string WindowsMouse = "Icon/aero_arrow.cur"; + } } diff --git a/tests/Images/Input/Icon/aero_arrow.cur b/tests/Images/Input/Icon/aero_arrow.cur new file mode 100644 index 000000000..82cbbd33e --- /dev/null +++ b/tests/Images/Input/Icon/aero_arrow.cur @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06678bbf954f0bece61062633dc63a52a34a6f3c27ac7108f28c0f0d26bb22a7 +size 136606 diff --git a/tests/Images/Input/Icon/flutter.ico b/tests/Images/Input/Icon/flutter.ico new file mode 100644 index 000000000..4001f1426 --- /dev/null +++ b/tests/Images/Input/Icon/flutter.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c098d3fc85cacff98b8e69811b48e9f0d852fcee278132d794411d978869cbf8 +size 33772 From ffde9a9380698c19b693847ad24f7789ccae9207 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 14 Dec 2023 14:13:53 +0800 Subject: [PATCH 50/79] Refactor decoder and add notes --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 4 +- .../Formats/Bmp/BmpDecoderOptions.cs | 12 +- .../Formats/Icon/Cur/CurDecoderCore.cs | 2 + .../Formats/Icon/Cur/CurFrameMetadata.cs | 2 +- .../Formats/Icon/Ico/IcoDecoderCore.cs | 2 + .../Formats/Icon/Ico/IcoFrameMetadata.cs | 8 +- src/ImageSharp/Formats/Icon/IconAssert.cs | 8 -- .../Formats/Icon/IconDecoderCore.cs | 107 ++++++++++++------ .../Formats/Icon/IconFrameCompression.cs | 5 - .../Formats/Icon/IconFrameMetadata.cs | 1 + src/ImageSharp/Metadata/ImageFrameMetadata.cs | 6 +- src/ImageSharp/Metadata/ImageMetadata.cs | 3 + .../Formats/Icon/Cur/CurDecoderTests.cs | 7 +- .../Formats/Icon/Ico/IcoDecoderTests.cs | 5 +- 14 files changed, 106 insertions(+), 66 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 9bea6867a..568eea00a 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -105,7 +105,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals /// private readonly bool skipFileHeader; - /// + /// private readonly bool isDoubleHeight; /// @@ -120,7 +120,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals this.memoryAllocator = this.configuration.MemoryAllocator; this.processedAlphaMask = options.ProcessedAlphaMask; this.skipFileHeader = options.SkipFileHeader; - this.isDoubleHeight = options.IsDoubleHeight; + this.isDoubleHeight = options.UseDoubleHeight; } /// diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs index 17d37cab2..158a9d479 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderOptions.cs @@ -18,10 +18,10 @@ public sealed class BmpDecoderOptions : ISpecializedDecoderOptions public RleSkippedPixelHandling RleSkippedPixelHandling { get; init; } /// - /// Gets a value indicating whether the additional AlphaMask is processed at decoding time. + /// Gets a value indicating whether the additional alpha mask is processed at decoding time. /// /// - /// It will be used at IcoDecoder. + /// Used by the icon decoder. /// internal bool ProcessedAlphaMask { get; init; } @@ -29,15 +29,15 @@ public sealed class BmpDecoderOptions : ISpecializedDecoderOptions /// Gets a value indicating whether to skip loading the BMP file header. /// /// - /// It will be used at IcoDecoder. + /// Used by the icon decoder. /// internal bool SkipFileHeader { get; init; } /// - /// Gets a value indicating whether the height is double of true height. + /// Gets a value indicating whether to treat the height as double of true height. /// /// - /// It will be used at IcoDecoder. + /// Used by the icon decoder. /// - internal bool IsDoubleHeight { get; init; } + internal bool UseDoubleHeight { get; init; } } diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs index 8b08f127d..44fcc8fcc 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs @@ -3,6 +3,8 @@ using SixLabors.ImageSharp.Metadata; +// TODO: flatten namespace. +// namespace SixLabors.ImageSharp.Formats.Cur; namespace SixLabors.ImageSharp.Formats.Icon.Cur; internal sealed class CurDecoderCore : IconDecoderCore diff --git a/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs b/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs index c94afdd3a..60ced16ea 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs +++ b/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs @@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp.Formats.Icon.Cur; /// -/// IcoFrameMetadata +/// IcoFrameMetadata. TODO: Remove base class and merge into this class. /// public class CurFrameMetadata : IconFrameMetadata, IDeepCloneable, IDeepCloneable { diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs index 0782c2128..677a9c07a 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs @@ -3,6 +3,8 @@ using SixLabors.ImageSharp.Metadata; +// TODO: flatten namespace. +// namespace SixLabors.ImageSharp.Formats.Ico; namespace SixLabors.ImageSharp.Formats.Icon.Ico; internal sealed class IcoDecoderCore : IconDecoderCore diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs index 7c903facd..4f557715d 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs +++ b/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs @@ -4,7 +4,7 @@ namespace SixLabors.ImageSharp.Formats.Icon.Ico; /// -/// IcoFrameMetadata +/// IcoFrameMetadata. TODO: Remove base class and merge into this class. /// public class IcoFrameMetadata : IconFrameMetadata, IDeepCloneable, IDeepCloneable { @@ -38,11 +38,9 @@ public class IcoFrameMetadata : IconFrameMetadata, IDeepCloneable - /// Gets or sets Specifies bits per pixel. + /// Gets or sets the bits per pixel. + /// TODO: This needs to be constrained and calculated using the metadata returned from the png/bmp. ///
- /// - /// It may used by Encoder. - /// public ushort BitCount { get => this.Field2; set => this.Field2 = value; } /// diff --git a/src/ImageSharp/Formats/Icon/IconAssert.cs b/src/ImageSharp/Formats/Icon/IconAssert.cs index bcb427c1a..04a9527b9 100644 --- a/src/ImageSharp/Formats/Icon/IconAssert.cs +++ b/src/ImageSharp/Formats/Icon/IconAssert.cs @@ -32,12 +32,4 @@ internal class IconAssert return v; } - - internal static void NotSquare(in Size size) - { - if (size.Width != size.Height) - { - throw new FormatException("This image is not square."); - } - } } diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index d9a578ff2..d7fc25c69 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -2,7 +2,8 @@ // Licensed under the Six Labors Split License. using System.Runtime.InteropServices; -using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -30,50 +31,63 @@ internal abstract class IconDecoderCore : IImageDecoderInternals long basePosition = stream.Position; this.ReadHeader(stream); - Span flag = stackalloc byte[Png.PngConstants.HeaderBytes.Length]; + Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; + Image result = new(this.Dimensions.Width, this.Dimensions.Height); - List<(Image Image, bool IsPng, int Index)> frames = new(this.Entries.Length); for (int i = 0; i < this.Entries.Length; i++) { - _ = IconAssert.EndOfStream(stream.Seek(basePosition + this.Entries[i].ImageOffset, SeekOrigin.Begin), basePosition + this.Entries[i].ImageOffset); - _ = IconAssert.EndOfStream(stream.Read(flag), Png.PngConstants.HeaderBytes.Length); - _ = stream.Seek(-Png.PngConstants.HeaderBytes.Length, SeekOrigin.Current); + ref IconDirEntry entry = ref this.Entries[i]; - bool isPng = flag.SequenceEqual(Png.PngConstants.HeaderBytes); + // If we hit the end of the stream we should break. + if (stream.Seek(basePosition + entry.ImageOffset, SeekOrigin.Begin) >= stream.Length) + { + break; + } - Image img = this.GetDecoder(isPng).Decode(stream, cancellationToken); - IconAssert.NotSquare(img.Size); - frames.Add((img, isPng, i)); - if (isPng && img.Size.Width > this.Dimensions.Width) + // There should always be enough bytes for this regardless of the entry type. + if (stream.Read(flag) != PngConstants.HeaderBytes.Length) { - this.Dimensions = img.Size; + break; } - } - ImageMetadata metadata = new(); - return new(this.Options.Configuration, metadata, frames.Select(i => - { - ImageFrame target = new(this.Options.Configuration, this.Dimensions); - ImageFrame source = i.Image.Frames.RootFrameUnsafe; + // Reset the stream position. + stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); + + bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); + using Image temp = this.GetDecoder(isPng).Decode(stream, cancellationToken); + + ImageFrame source = temp.Frames.RootFrameUnsafe; + ImageFrame target = i == 0 ? result.Frames.RootFrameUnsafe : result.Frames.CreateFrame(); + + // Draw the new frame at position 0,0. We capture the dimensions for cropping during encoding + // via the icon entry. for (int h = 0; h < source.Height; h++) { source.PixelBuffer.DangerousGetRowSpan(h).CopyTo(target.PixelBuffer.DangerousGetRowSpan(h)); } - if (i.IsPng) + // Copy the format specific metadata to the image. + if (isPng) { - target.Metadata.UnsafeSetFormatMetadata(Png.PngFormat.Instance, i.Image.Metadata.GetPngMetadata()); + if (i == 0) + { + result.Metadata.SetFormatMetadata(PngFormat.Instance, temp.Metadata.GetPngMetadata()); + } + + target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngFrameMetadata()); } - else + else if (i == 0) { - target.Metadata.UnsafeSetFormatMetadata(Bmp.BmpFormat.Instance, i.Image.Metadata.GetBmpMetadata()); + // Bmp does not contain frame specific metadata. + result.Metadata.SetFormatMetadata(BmpFormat.Instance, temp.Metadata.GetBmpMetadata()); } - this.GetFrameMetadata(target.Metadata).FromIconDirEntry(this.Entries[i.Index]); + // TODO: The inheriting decoder should be responsible for setting the actual data (FromIconDirEntry) + // so we can avoid the protected Field1 and Field2 properties and use strong typing. + this.GetFrameMetadata(target.Metadata).FromIconDirEntry(entry); + } - i.Image.Dispose(); - return target; - }).ToArray()); + return result; } public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) @@ -84,11 +98,14 @@ internal abstract class IconDecoderCore : IImageDecoderInternals ImageFrameMetadata[] frames = new ImageFrameMetadata[this.FileHeader.Count]; for (int i = 0; i < frames.Length; i++) { + // TODO: Use the Identify methods in each decoder to return the + // format specific metadata for the image and frame. frames[i] = new(); IconFrameMetadata icoFrameMetadata = this.GetFrameMetadata(frames[i]); icoFrameMetadata.FromIconDirEntry(this.Entries[i]); } + // TODO: Use real values from the metadata. return new(new(32), new(0), metadata, frames); } @@ -96,6 +113,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals protected void ReadHeader(Stream stream) { + // TODO: Check length and throw if the header cannot be read. _ = Read(stream, out this.fileHeader, IconDir.Size); this.Entries = new IconDirEntry[this.FileHeader.Count]; for (int i = 0; i < this.Entries.Length; i++) @@ -103,15 +121,39 @@ internal abstract class IconDecoderCore : IImageDecoderInternals _ = Read(stream, out this.Entries[i], IconDirEntry.Size); } - this.Dimensions = new( - this.Entries.Max(i => i.Width), - this.Entries.Max(i => i.Height)); + int width = 0; + int height = 0; + foreach (IconDirEntry entry in this.Entries) + { + if (entry.Width == 0) + { + width = 256; + } + + if (entry.Height == 0) + { + height = 256; + } + + if (width == 256 && height == 256) + { + break; + } + + width = Math.Max(width, entry.Width); + height = Math.Max(height, entry.Height); + } + + this.Dimensions = new(width, height); } private static int Read(Stream stream, out T data, int size) where T : unmanaged { + // TODO: Use explicit parsing methods for each T type. + // See PngHeader.Parse() for example. Span buffer = stackalloc byte[size]; + _ = IconAssert.EndOfStream(stream.Read(buffer), size); data = MemoryMarshal.Cast(buffer)[0]; return size; @@ -121,15 +163,16 @@ internal abstract class IconDecoderCore : IImageDecoderInternals { if (isPng) { - return new Png.PngDecoderCore(this.Options); + return new PngDecoderCore(this.Options); } else { - return new Bmp.BmpDecoderCore(new() + return new BmpDecoderCore(new() { + GeneralOptions = this.Options, ProcessedAlphaMask = true, SkipFileHeader = true, - IsDoubleHeight = true, + UseDoubleHeight = true, }); } } diff --git a/src/ImageSharp/Formats/Icon/IconFrameCompression.cs b/src/ImageSharp/Formats/Icon/IconFrameCompression.cs index f6509f40c..5c772c3fe 100644 --- a/src/ImageSharp/Formats/Icon/IconFrameCompression.cs +++ b/src/ImageSharp/Formats/Icon/IconFrameCompression.cs @@ -8,11 +8,6 @@ namespace SixLabors.ImageSharp.Formats.Icon; ///
public enum IconFrameCompression { - /// - /// Unknown - /// - Unknown, - /// /// Bmp /// diff --git a/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs b/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs index 17e641c71..5c23388e7 100644 --- a/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs +++ b/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs @@ -5,6 +5,7 @@ namespace SixLabors.ImageSharp.Formats.Icon; /// /// IconFrameMetadata +/// TODO: Delete this. Treat the individual metadata types as separate types so we can avoid Field1, Field2 and use strong typing with constaints. /// public abstract class IconFrameMetadata : IDeepCloneable { diff --git a/src/ImageSharp/Metadata/ImageFrameMetadata.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs index 0567c8916..562e47803 100644 --- a/src/ImageSharp/Metadata/ImageFrameMetadata.cs +++ b/src/ImageSharp/Metadata/ImageFrameMetadata.cs @@ -99,9 +99,9 @@ public sealed class ImageFrameMetadata : IDeepCloneable return newMeta; } - internal void UnsafeSetFormatMetadata( - IImageFormat key, - IDeepCloneable value) + internal void SetFormatMetadata(IImageFormat key, TFormatFrameMetadata value) + where TFormatMetadata : class + where TFormatFrameMetadata : class, IDeepCloneable => this.formatMetadata[key] = value; /// diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs index 6b62be08f..d9aba6631 100644 --- a/src/ImageSharp/Metadata/ImageMetadata.cs +++ b/src/ImageSharp/Metadata/ImageMetadata.cs @@ -215,6 +215,9 @@ public sealed class ImageMetadata : IDeepCloneable metadata = default; return false; } + internal void SetFormatMetadata(IImageFormat key, TFormatMetadata value) + where TFormatMetadata : class, IDeepCloneable + => this.formatMetadata[key] = value; /// public ImageMetadata DeepClone() => new(this); diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs index c3c9ad1c4..f78c04cdf 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Formats.Icon.Cur; -using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Cur; @@ -18,6 +17,10 @@ public class CurDecoderTests { using Image image = provider.GetImage(CurDecoder.Instance); - image.DebugSave(provider, extension: "tiff", encoder: new TiffEncoder()); + image.DebugSaveMultiFrame(provider, extension: "png"); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index 6e7b35111..86f8b003b 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Formats.Icon.Ico; -using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Ico; @@ -18,6 +17,8 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSave(provider, extension: "tiff", encoder: new TiffEncoder()); + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc } } From 79f538723f6500350be073bf66bfc22d65784d56 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 14 Dec 2023 14:13:54 +0800 Subject: [PATCH 51/79] Handle frames exceeding 256 pixels --- .../Formats/Icon/IconDecoderCore.cs | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index d7fc25c69..874e93411 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -32,7 +32,8 @@ internal abstract class IconDecoderCore : IImageDecoderInternals this.ReadHeader(stream); Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; - Image result = new(this.Dimensions.Width, this.Dimensions.Height); + + List<(Image Image, bool IsPng, int Index)> decodedEntries = new(this.Entries.Length); for (int i = 0; i < this.Entries.Length; i++) { @@ -54,37 +55,62 @@ internal abstract class IconDecoderCore : IImageDecoderInternals stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); - using Image temp = this.GetDecoder(isPng).Decode(stream, cancellationToken); - ImageFrame source = temp.Frames.RootFrameUnsafe; - ImageFrame target = i == 0 ? result.Frames.RootFrameUnsafe : result.Frames.CreateFrame(); + // Decode the frame into a temp image buffer. This is disposed after the frame is copied to the result. + Image temp = this.GetDecoder(isPng).Decode(stream, cancellationToken); + decodedEntries.Add((temp, isPng, i)); + + // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data + // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. + this.Dimensions = new(Math.Max(this.Dimensions.Width, temp.Size.Width), Math.Max(this.Dimensions.Height, temp.Size.Height)); + } - // Draw the new frame at position 0,0. We capture the dimensions for cropping during encoding - // via the icon entry. - for (int h = 0; h < source.Height; h++) + ImageMetadata metadata = new(); + BmpMetadata? bmpMetadata = null; + PngMetadata? pngMetadata = null; + Image result = new(this.Options.Configuration, metadata, decodedEntries.Select(x => + { + ImageFrame target = new(this.Options.Configuration, this.Dimensions); + ImageFrame source = x.Image.Frames.RootFrameUnsafe; + for (int y = 0; y < source.Height; y++) { - source.PixelBuffer.DangerousGetRowSpan(h).CopyTo(target.PixelBuffer.DangerousGetRowSpan(h)); + source.PixelBuffer.DangerousGetRowSpan(y).CopyTo(target.PixelBuffer.DangerousGetRowSpan(y)); } - // Copy the format specific metadata to the image. - if (isPng) + // Copy the format specific frame metadata to the image. + if (x.IsPng) { - if (i == 0) + if (x.Index == 0) { - result.Metadata.SetFormatMetadata(PngFormat.Instance, temp.Metadata.GetPngMetadata()); + pngMetadata = x.Image.Metadata.GetPngMetadata(); } + // Bmp does not contain frame specific metadata. target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngFrameMetadata()); } - else if (i == 0) + else if (x.Index == 0) { - // Bmp does not contain frame specific metadata. - result.Metadata.SetFormatMetadata(BmpFormat.Instance, temp.Metadata.GetBmpMetadata()); + bmpMetadata = x.Image.Metadata.GetBmpMetadata(); } // TODO: The inheriting decoder should be responsible for setting the actual data (FromIconDirEntry) // so we can avoid the protected Field1 and Field2 properties and use strong typing. - this.GetFrameMetadata(target.Metadata).FromIconDirEntry(entry); + this.GetFrameMetadata(target.Metadata).FromIconDirEntry(this.Entries[x.Index]); + + x.Image.Dispose(); + + return target; + }).ToArray()); + + // Copy the format specific metadata to the image. + if (bmpMetadata != null) + { + result.Metadata.SetFormatMetadata(BmpFormat.Instance, bmpMetadata); + } + + if (pngMetadata != null) + { + result.Metadata.SetFormatMetadata(PngFormat.Instance, pngMetadata); } return result; @@ -125,6 +151,8 @@ internal abstract class IconDecoderCore : IImageDecoderInternals int height = 0; foreach (IconDirEntry entry in this.Entries) { + // Since Windows 95 size of an image in the ICONDIRENTRY structure might + // be set to zero, which means 256 pixels. if (entry.Width == 0) { width = 256; From a811784d34c87e46322f8b8e1d8b3f48ebe9e3ef Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 14 Dec 2023 14:13:55 +0800 Subject: [PATCH 52/79] Update TgaFileHeaderTests.cs --- tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs index bf24ba350..2f96b6d43 100644 --- a/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tga/TgaFileHeaderTests.cs @@ -9,6 +9,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tga; [Trait("Format", "Tga")] public class TgaFileHeaderTests { + // TODO: Some of these clash with the ICO magic bytes. Check correctness. + // https://en.wikipedia.org/wiki/Truevision_TGA#Header [Theory] [InlineData(new byte[] { 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 195, 0, 32, 8 })] // invalid tga image type. [InlineData(new byte[] { 0, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 195, 0, 32, 8 })] // invalid colormap type. From 43ea25fe8b612cf1da5713b1149ac2a078be64e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:55 +0800 Subject: [PATCH 53/79] Add Parse Method and Check Stream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/IconDecoderCore.cs | 24 +++++++------------ src/ImageSharp/Formats/Icon/IconDir.cs | 15 +++++++++--- src/ImageSharp/Formats/Icon/IconDirEntry.cs | 3 +++ 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 874e93411..1077bde51 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -139,12 +139,18 @@ internal abstract class IconDecoderCore : IImageDecoderInternals protected void ReadHeader(Stream stream) { - // TODO: Check length and throw if the header cannot be read. - _ = Read(stream, out this.fileHeader, IconDir.Size); + Span buffer = stackalloc byte[IconDirEntry.Size]; + + // ICONDIR + _ = IconAssert.EndOfStream(stream.Read(buffer[..IconDir.Size]), IconDir.Size); + this.fileHeader = IconDir.Parse(buffer); + + // ICONDIRENTRY this.Entries = new IconDirEntry[this.FileHeader.Count]; for (int i = 0; i < this.Entries.Length; i++) { - _ = Read(stream, out this.Entries[i], IconDirEntry.Size); + _ = IconAssert.EndOfStream(stream.Read(buffer[..IconDirEntry.Size]), IconDirEntry.Size); + this.Entries[i] = IconDirEntry.Parse(buffer); } int width = 0; @@ -175,18 +181,6 @@ internal abstract class IconDecoderCore : IImageDecoderInternals this.Dimensions = new(width, height); } - private static int Read(Stream stream, out T data, int size) - where T : unmanaged - { - // TODO: Use explicit parsing methods for each T type. - // See PngHeader.Parse() for example. - Span buffer = stackalloc byte[size]; - - _ = IconAssert.EndOfStream(stream.Read(buffer), size); - data = MemoryMarshal.Cast(buffer)[0]; - return size; - } - private IImageDecoderInternals GetDecoder(bool isPng) { if (isPng) diff --git a/src/ImageSharp/Formats/Icon/IconDir.cs b/src/ImageSharp/Formats/Icon/IconDir.cs index f1281a568..53b87bc9f 100644 --- a/src/ImageSharp/Formats/Icon/IconDir.cs +++ b/src/ImageSharp/Formats/Icon/IconDir.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Runtime.InteropServices; @@ -15,12 +15,21 @@ internal struct IconDir public IconDir(IconFileType type) : this(type, 0) - => this.Type = type; + { + } public IconDir(IconFileType type, ushort count) + : this(0, type, count) + { + } + + public IconDir(ushort reserved, IconFileType type, ushort count) { - this.Reserved = 0; + this.Reserved = reserved; this.Type = type; this.Count = count; } + + public static IconDir Parse(in ReadOnlySpan data) + => MemoryMarshal.Cast(data)[0]; } diff --git a/src/ImageSharp/Formats/Icon/IconDirEntry.cs b/src/ImageSharp/Formats/Icon/IconDirEntry.cs index 43254f89d..edd778f7e 100644 --- a/src/ImageSharp/Formats/Icon/IconDirEntry.cs +++ b/src/ImageSharp/Formats/Icon/IconDirEntry.cs @@ -25,4 +25,7 @@ internal struct IconDirEntry public uint BytesInRes; public uint ImageOffset; + + public static IconDirEntry Parse(in ReadOnlySpan data) + => MemoryMarshal.Cast(data)[0]; } From 05d33f2895db1505940b92427b164091088cece6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:56 +0800 Subject: [PATCH 54/79] Remove IconFrameMetadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/Cur/CurDecoderCore.cs | 2 +- .../Formats/Icon/Cur/CurFrameMetadata.cs | 90 ++++++++++++--- .../Formats/Icon/Ico/IcoDecoderCore.cs | 3 +- .../Formats/Icon/Ico/IcoFrameMetadata.cs | 80 +++++++++++--- .../Formats/Icon/IconDecoderCore.cs | 9 +- .../Formats/Icon/IconFrameMetadata.cs | 104 ------------------ 6 files changed, 140 insertions(+), 148 deletions(-) delete mode 100644 src/ImageSharp/Formats/Icon/IconFrameMetadata.cs diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs index 44fcc8fcc..afd751279 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs @@ -14,5 +14,5 @@ internal sealed class CurDecoderCore : IconDecoderCore { } - protected override IconFrameMetadata GetFrameMetadata(ImageFrameMetadata metadata) => metadata.GetCurMetadata(); + protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry) => metadata.GetCurMetadata().FromIconDirEntry(entry); } diff --git a/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs b/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs index 60ced16ea..2d1c22776 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs +++ b/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs @@ -4,9 +4,9 @@ namespace SixLabors.ImageSharp.Formats.Icon.Cur; /// -/// IcoFrameMetadata. TODO: Remove base class and merge into this class. +/// IcoFrameMetadata. /// -public class CurFrameMetadata : IconFrameMetadata, IDeepCloneable, IDeepCloneable +public class CurFrameMetadata : IDeepCloneable, IDeepCloneable { /// /// Initializes a new instance of the class. @@ -15,38 +15,92 @@ public class CurFrameMetadata : IconFrameMetadata, IDeepCloneable - /// Initializes a new instance of the class. - /// - /// metadata - public CurFrameMetadata(IconFrameMetadata metadata) - : base(metadata) - { - } - /// /// Initializes a new instance of the class. /// /// width /// height /// colorCount - /// field1 - /// field2 - public CurFrameMetadata(byte width, byte height, byte colorCount, ushort field1, ushort field2) - : base(width, height, colorCount, field1, field2) + /// hotspotX + /// hotspotY + public CurFrameMetadata(byte width, byte height, byte colorCount, ushort hotspotX, ushort hotspotY) { + this.EncodingWidth = width; + this.EncodingHeight = height; + this.ColorCount = colorCount; + this.HotspotX = hotspotX; + this.HotspotY = hotspotY; } + /// + public CurFrameMetadata(CurFrameMetadata metadata) + { + this.EncodingWidth = metadata.EncodingWidth; + this.EncodingHeight = metadata.EncodingHeight; + this.ColorCount = metadata.ColorCount; + this.HotspotX = metadata.HotspotX; + this.HotspotY = metadata.HotspotY; + this.Compression = metadata.Compression; + } + + /// + /// Gets or sets icoFrameCompression. + /// + public IconFrameCompression Compression { get; set; } + + /// + /// Gets or sets ColorCount field.
+ /// Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette. + ///
+ // TODO: BmpMetadata does not supported palette yet. + public byte ColorCount { get; set; } + /// /// Gets or sets Specifies the horizontal coordinates of the hotspot in number of pixels from the left. /// - public ushort HotspotX { get => this.Field1; set => this.Field1 = value; } + public ushort HotspotX { get; set; } /// /// Gets or sets Specifies the vertical coordinates of the hotspot in number of pixels from the top. /// - public ushort HotspotY { get => this.Field2; set => this.Field2 = value; } + public ushort HotspotY { get; set; } + + /// + /// Gets or sets Height field.
+ /// Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels. + ///
+ public byte EncodingHeight { get; set; } + + /// + /// Gets or sets Width field.
+ /// Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels. + ///
+ public byte EncodingWidth { get; set; } + + /// + public Bmp.BmpBitsPerPixel BitsPerPixel { get; set; } = Bmp.BmpBitsPerPixel.Pixel24; /// - public override CurFrameMetadata DeepClone() => new(this); + public CurFrameMetadata DeepClone() => new(this); + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + internal void FromIconDirEntry(in IconDirEntry entry) + { + this.EncodingWidth = entry.Width; + this.EncodingHeight = entry.Height; + this.ColorCount = entry.ColorCount; + this.HotspotX = entry.Planes; + this.HotspotY = entry.BitCount; + } + + internal IconDirEntry ToIconDirEntry() => new() + { + Width = this.EncodingWidth, + Height = this.EncodingHeight, + ColorCount = this.ColorCount, + Planes = this.HotspotX, + BitCount = this.HotspotY, + }; } diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs index 677a9c07a..4884fdf9a 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs @@ -14,5 +14,6 @@ internal sealed class IcoDecoderCore : IconDecoderCore { } - protected override IconFrameMetadata GetFrameMetadata(ImageFrameMetadata metadata) => metadata.GetIcoMetadata(); + protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry) + => metadata.GetIcoMetadata().FromIconDirEntry(entry); } diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs index 4f557715d..82e3c1db3 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs +++ b/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Icon.Ico; /// /// IcoFrameMetadata. TODO: Remove base class and merge into this class. /// -public class IcoFrameMetadata : IconFrameMetadata, IDeepCloneable, IDeepCloneable +public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable { /// /// Initializes a new instance of the class. @@ -15,34 +15,78 @@ public class IcoFrameMetadata : IconFrameMetadata, IDeepCloneable - /// Initializes a new instance of the class. - /// - /// metadata - public IcoFrameMetadata(IconFrameMetadata metadata) - : base(metadata) - { - } - /// /// Initializes a new instance of the class. /// /// width /// height /// colorCount - /// field1 - /// field2 - public IcoFrameMetadata(byte width, byte height, byte colorCount, ushort field1, ushort field2) - : base(width, height, colorCount, field1, field2) + public IcoFrameMetadata(byte width, byte height, byte colorCount) { + this.EncodingWidth = width; + this.EncodingHeight = height; + this.ColorCount = colorCount; } + /// + public IcoFrameMetadata(IcoFrameMetadata metadata) + { + this.EncodingWidth = metadata.EncodingWidth; + this.EncodingHeight = metadata.EncodingHeight; + this.ColorCount = metadata.ColorCount; + this.Compression = metadata.Compression; + } + + /// + /// Gets or sets icoFrameCompression. + /// + public IconFrameCompression Compression { get; set; } + + /// + /// Gets or sets ColorCount field.
+ /// Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette. + ///
+ // TODO: BmpMetadata does not supported palette yet. + public byte ColorCount { get; set; } + /// - /// Gets or sets the bits per pixel. - /// TODO: This needs to be constrained and calculated using the metadata returned from the png/bmp. + /// Gets or sets Height field.
+ /// Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels. ///
- public ushort BitCount { get => this.Field2; set => this.Field2 = value; } + public byte EncodingHeight { get; set; } + + /// + /// Gets or sets Width field.
+ /// Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels. + ///
+ public byte EncodingWidth { get; set; } + + /// + public Bmp.BmpBitsPerPixel BitsPerPixel { get; set; } = Bmp.BmpBitsPerPixel.Pixel24; /// - public override IcoFrameMetadata DeepClone() => new(this); + public IcoFrameMetadata DeepClone() => new(this); + + /// + IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); + + internal void FromIconDirEntry(in IconDirEntry entry) + { + this.EncodingWidth = entry.Width; + this.EncodingHeight = entry.Height; + this.ColorCount = entry.ColorCount; + } + + internal IconDirEntry ToIconDirEntry() => new() + { + Width = this.EncodingWidth, + Height = this.EncodingHeight, + ColorCount = this.ColorCount, + Planes = 1, + BitCount = this.Compression switch + { + IconFrameCompression.Bmp => (ushort)this.BitsPerPixel, + _ => 0, + }, + }; } diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 1077bde51..21c0a3bc4 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -93,9 +93,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals bmpMetadata = x.Image.Metadata.GetBmpMetadata(); } - // TODO: The inheriting decoder should be responsible for setting the actual data (FromIconDirEntry) - // so we can avoid the protected Field1 and Field2 properties and use strong typing. - this.GetFrameMetadata(target.Metadata).FromIconDirEntry(this.Entries[x.Index]); + this.SetFrameMetadata(target.Metadata, this.Entries[x.Index]); x.Image.Dispose(); @@ -127,15 +125,14 @@ internal abstract class IconDecoderCore : IImageDecoderInternals // TODO: Use the Identify methods in each decoder to return the // format specific metadata for the image and frame. frames[i] = new(); - IconFrameMetadata icoFrameMetadata = this.GetFrameMetadata(frames[i]); - icoFrameMetadata.FromIconDirEntry(this.Entries[i]); + this.SetFrameMetadata(frames[i], this.Entries[i]); } // TODO: Use real values from the metadata. return new(new(32), new(0), metadata, frames); } - protected abstract IconFrameMetadata GetFrameMetadata(ImageFrameMetadata metadata); + protected abstract void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry); protected void ReadHeader(Stream stream) { diff --git a/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs b/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs deleted file mode 100644 index 5c23388e7..000000000 --- a/src/ImageSharp/Formats/Icon/IconFrameMetadata.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Icon; - -/// -/// IconFrameMetadata -/// TODO: Delete this. Treat the individual metadata types as separate types so we can avoid Field1, Field2 and use strong typing with constaints. -/// -public abstract class IconFrameMetadata : IDeepCloneable -{ - /// - /// Initializes a new instance of the class. - /// - protected IconFrameMetadata() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// width - /// height - /// colorCount - /// field1 - /// field2 - protected IconFrameMetadata(byte width, byte height, byte colorCount, ushort field1, ushort field2) - { - this.EncodingWidth = width; - this.EncodingHeight = height; - this.ColorCount = colorCount; - this.Field1 = field1; - this.Field2 = field2; - } - - /// - protected IconFrameMetadata(IconFrameMetadata metadata) - { - this.EncodingWidth = metadata.EncodingWidth; - this.EncodingHeight = metadata.EncodingHeight; - this.ColorCount = metadata.ColorCount; - this.Field1 = metadata.Field1; - this.Field2 = metadata.Field2; - } - - /// - /// Gets or sets icoFrameCompression. - /// - public IconFrameCompression Compression { get; protected set; } - - /// - /// Gets or sets ColorCount field.
- /// Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette. - ///
- // TODO: BmpMetadata does not supported palette yet. - public byte ColorCount { get; set; } - - /// - /// Gets or sets Planes field.
- /// In ICO format: Specifies color planes. Should be 0 or 1.
- /// In CUR format: Specifies the horizontal coordinates of the hotspot in number of pixels from the left. - ///
- protected ushort Field1 { get; set; } - - /// - /// Gets or sets BitCount field.
- /// In ICO format: Specifies bits per pixel.
- /// In CUR format: Specifies the vertical coordinates of the hotspot in number of pixels from the top. - ///
- protected ushort Field2 { get; set; } - - /// - /// Gets or sets Height field.
- /// Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels. - ///
- public byte EncodingHeight { get; set; } - - /// - /// Gets or sets Width field.
- /// Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels. - ///
- public byte EncodingWidth { get; set; } - - /// - public abstract IDeepCloneable DeepClone(); - - internal void FromIconDirEntry(in IconDirEntry metadata) - { - this.EncodingWidth = metadata.Width; - this.EncodingHeight = metadata.Height; - this.ColorCount = metadata.ColorCount; - this.Field1 = metadata.Planes; - this.Field2 = metadata.BitCount; - } - - internal IconDirEntry ToIconDirEntry() => new() - { - Width = this.EncodingWidth, - Height = this.EncodingHeight, - ColorCount = this.ColorCount, - Planes = this.Field1, - BitCount = this.Field2, - }; -} From 729d64ec757536c30c3f3ed5255f2460762206d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:56 +0800 Subject: [PATCH 55/79] Refactor SetFrameMetadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/Cur/CurDecoderCore.cs | 8 ++- .../Formats/Icon/Ico/IcoDecoderCore.cs | 9 ++- .../Formats/Icon/IconDecoderCore.cs | 64 +++++++++++++------ 3 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs index afd751279..2ed89483a 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs @@ -14,5 +14,11 @@ internal sealed class CurDecoderCore : IconDecoderCore { } - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry) => metadata.GetCurMetadata().FromIconDirEntry(entry); + protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, Bmp.BmpBitsPerPixel bitsPerPixel) + { + CurFrameMetadata curFrameMetadata = metadata.GetCurMetadata(); + curFrameMetadata.FromIconDirEntry(entry); + curFrameMetadata.Compression = compression; + curFrameMetadata.BitsPerPixel = bitsPerPixel; + } } diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs index 4884fdf9a..c7841ce39 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs @@ -14,6 +14,11 @@ internal sealed class IcoDecoderCore : IconDecoderCore { } - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry) - => metadata.GetIcoMetadata().FromIconDirEntry(entry); + protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, Bmp.BmpBitsPerPixel bitsPerPixel) + { + IcoFrameMetadata icoFrameMetadata = metadata.GetIcoMetadata(); + icoFrameMetadata.FromIconDirEntry(entry); + icoFrameMetadata.Compression = compression; + icoFrameMetadata.BitsPerPixel = bitsPerPixel; + } } diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 21c0a3bc4..6af9553cc 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -33,7 +33,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; - List<(Image Image, bool IsPng, int Index)> decodedEntries = new(this.Entries.Length); + List<(Image Image, IconFrameCompression Compression, int Index)> decodedEntries = new(this.Entries.Length); for (int i = 0; i < this.Entries.Length; i++) { @@ -58,7 +58,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals // Decode the frame into a temp image buffer. This is disposed after the frame is copied to the result. Image temp = this.GetDecoder(isPng).Decode(stream, cancellationToken); - decodedEntries.Add((temp, isPng, i)); + decodedEntries.Add((temp, isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, i)); // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. @@ -66,10 +66,10 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } ImageMetadata metadata = new(); - BmpMetadata? bmpMetadata = null; PngMetadata? pngMetadata = null; Image result = new(this.Options.Configuration, metadata, decodedEntries.Select(x => { + BmpBitsPerPixel bitsPerPixel = default; ImageFrame target = new(this.Options.Configuration, this.Dimensions); ImageFrame source = x.Image.Frames.RootFrameUnsafe; for (int y = 0; y < source.Height; y++) @@ -78,7 +78,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } // Copy the format specific frame metadata to the image. - if (x.IsPng) + if (x.Compression is IconFrameCompression.Png) { if (x.Index == 0) { @@ -88,12 +88,12 @@ internal abstract class IconDecoderCore : IImageDecoderInternals // Bmp does not contain frame specific metadata. target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngFrameMetadata()); } - else if (x.Index == 0) + else { - bmpMetadata = x.Image.Metadata.GetBmpMetadata(); + bitsPerPixel = x.Image.Metadata.GetBmpMetadata().BitsPerPixel; } - this.SetFrameMetadata(target.Metadata, this.Entries[x.Index]); + this.SetFrameMetadata(target.Metadata, this.Entries[x.Index], x.Compression, bitsPerPixel); x.Image.Dispose(); @@ -101,11 +101,6 @@ internal abstract class IconDecoderCore : IImageDecoderInternals }).ToArray()); // Copy the format specific metadata to the image. - if (bmpMetadata != null) - { - result.Metadata.SetFormatMetadata(BmpFormat.Instance, bmpMetadata); - } - if (pngMetadata != null) { result.Metadata.SetFormatMetadata(PngFormat.Instance, pngMetadata); @@ -116,23 +111,56 @@ internal abstract class IconDecoderCore : IImageDecoderInternals public ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken) { + // Stream may not at 0. + long basePosition = stream.Position; this.ReadHeader(stream); + Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; + ImageMetadata metadata = new(); ImageFrameMetadata[] frames = new ImageFrameMetadata[this.FileHeader.Count]; for (int i = 0; i < frames.Length; i++) { - // TODO: Use the Identify methods in each decoder to return the - // format specific metadata for the image and frame. + BmpBitsPerPixel bitsPerPixel = default; + ref IconDirEntry entry = ref this.Entries[i]; + + // If we hit the end of the stream we should break. + if (stream.Seek(basePosition + entry.ImageOffset, SeekOrigin.Begin) >= stream.Length) + { + break; + } + + // There should always be enough bytes for this regardless of the entry type. + if (stream.Read(flag) != PngConstants.HeaderBytes.Length) + { + break; + } + + // Reset the stream position. + stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); + + bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); + + // Decode the frame into a temp image buffer. This is disposed after the frame is copied to the result. + ImageInfo temp = this.GetDecoder(isPng).Identify(stream, cancellationToken); + frames[i] = new(); - this.SetFrameMetadata(frames[i], this.Entries[i]); + if (isPng) + { + bitsPerPixel = temp.Metadata.GetBmpMetadata().BitsPerPixel; + } + + this.SetFrameMetadata(frames[i], this.Entries[i], isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, bitsPerPixel); + + // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data + // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. + this.Dimensions = new(Math.Max(this.Dimensions.Width, temp.Size.Width), Math.Max(this.Dimensions.Height, temp.Size.Height)); } - // TODO: Use real values from the metadata. - return new(new(32), new(0), metadata, frames); + return new(new(32), this.Dimensions, metadata, frames); } - protected abstract void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry); + protected abstract void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel); protected void ReadHeader(Stream stream) { From 63da967c7f8e41467243993ef64cc45079f46b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:57 +0800 Subject: [PATCH 56/79] Flatten namespace. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- src/ImageSharp/Configuration.cs | 4 ++-- .../Formats/{Icon => }/Cur/CurConfigurationModule.cs | 4 +++- src/ImageSharp/Formats/{Icon => }/Cur/CurConstants.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Cur/CurDecoder.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Cur/CurDecoderCore.cs | 5 ++--- src/ImageSharp/Formats/{Icon => }/Cur/CurFormat.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Cur/CurFrameMetadata.cs | 4 +++- src/ImageSharp/Formats/{Icon => }/Cur/CurMetadata.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Cur/MetadataExtensions.cs | 2 +- .../Formats/{Icon => }/Ico/IcoConfigurationModule.cs | 4 +++- src/ImageSharp/Formats/{Icon => }/Ico/IcoConstants.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Ico/IcoDecoder.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Ico/IcoDecoderCore.cs | 5 ++--- src/ImageSharp/Formats/{Icon => }/Ico/IcoFormat.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Ico/IcoFrameMetadata.cs | 4 +++- src/ImageSharp/Formats/{Icon => }/Ico/IcoMetadata.cs | 2 +- src/ImageSharp/Formats/{Icon => }/Ico/MetadataExtensions.cs | 2 +- tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs | 4 +--- tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs | 2 +- 19 files changed, 30 insertions(+), 26 deletions(-) rename src/ImageSharp/Formats/{Icon => }/Cur/CurConfigurationModule.cs (88%) rename src/ImageSharp/Formats/{Icon => }/Cur/CurConstants.cs (93%) rename src/ImageSharp/Formats/{Icon => }/Cur/CurDecoder.cs (96%) rename src/ImageSharp/Formats/{Icon => }/Cur/CurDecoderCore.cs (84%) rename src/ImageSharp/Formats/{Icon => }/Cur/CurFormat.cs (95%) rename src/ImageSharp/Formats/{Icon => }/Cur/CurFrameMetadata.cs (97%) rename src/ImageSharp/Formats/{Icon => }/Cur/CurMetadata.cs (89%) rename src/ImageSharp/Formats/{Icon => }/Cur/MetadataExtensions.cs (97%) rename src/ImageSharp/Formats/{Icon => }/Ico/IcoConfigurationModule.cs (88%) rename src/ImageSharp/Formats/{Icon => }/Ico/IcoConstants.cs (95%) rename src/ImageSharp/Formats/{Icon => }/Ico/IcoDecoder.cs (96%) rename src/ImageSharp/Formats/{Icon => }/Ico/IcoDecoderCore.cs (84%) rename src/ImageSharp/Formats/{Icon => }/Ico/IcoFormat.cs (95%) rename src/ImageSharp/Formats/{Icon => }/Ico/IcoFrameMetadata.cs (97%) rename src/ImageSharp/Formats/{Icon => }/Ico/IcoMetadata.cs (89%) rename src/ImageSharp/Formats/{Icon => }/Ico/MetadataExtensions.cs (97%) diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs index d6cfd480d..1d9f3bb85 100644 --- a/src/ImageSharp/Configuration.cs +++ b/src/ImageSharp/Configuration.cs @@ -4,9 +4,9 @@ using System.Collections.Concurrent; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.Formats.Icon.Cur; -using SixLabors.ImageSharp.Formats.Icon.Ico; +using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Pbm; using SixLabors.ImageSharp.Formats.Png; diff --git a/src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs b/src/ImageSharp/Formats/Cur/CurConfigurationModule.cs similarity index 88% rename from src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs rename to src/ImageSharp/Formats/Cur/CurConfigurationModule.cs index c975bc609..1c7db4bab 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurConfigurationModule.cs +++ b/src/ImageSharp/Formats/Cur/CurConfigurationModule.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +using SixLabors.ImageSharp.Formats.Icon; + +namespace SixLabors.ImageSharp.Formats.Cur; /// /// Registers the image encoders, decoders and mime type detectors for the Ico format. diff --git a/src/ImageSharp/Formats/Icon/Cur/CurConstants.cs b/src/ImageSharp/Formats/Cur/CurConstants.cs similarity index 93% rename from src/ImageSharp/Formats/Icon/Cur/CurConstants.cs rename to src/ImageSharp/Formats/Cur/CurConstants.cs index 701b40cf4..6efd2817c 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurConstants.cs +++ b/src/ImageSharp/Formats/Cur/CurConstants.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +namespace SixLabors.ImageSharp.Formats.Cur; /// /// Defines constants relating to ICOs diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs b/src/ImageSharp/Formats/Cur/CurDecoder.cs similarity index 96% rename from src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs rename to src/ImageSharp/Formats/Cur/CurDecoder.cs index ceefdcaba..cbe646c47 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurDecoder.cs +++ b/src/ImageSharp/Formats/Cur/CurDecoder.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +namespace SixLabors.ImageSharp.Formats.Cur; /// /// Decoder for generating an image out of a ico encoded stream. diff --git a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs similarity index 84% rename from src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs rename to src/ImageSharp/Formats/Cur/CurDecoderCore.cs index 2ed89483a..4c0224652 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs @@ -1,11 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Icon; using SixLabors.ImageSharp.Metadata; -// TODO: flatten namespace. -// namespace SixLabors.ImageSharp.Formats.Cur; -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +namespace SixLabors.ImageSharp.Formats.Cur; internal sealed class CurDecoderCore : IconDecoderCore { diff --git a/src/ImageSharp/Formats/Icon/Cur/CurFormat.cs b/src/ImageSharp/Formats/Cur/CurFormat.cs similarity index 95% rename from src/ImageSharp/Formats/Icon/Cur/CurFormat.cs rename to src/ImageSharp/Formats/Cur/CurFormat.cs index 1e5758bc4..af93382ec 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurFormat.cs +++ b/src/ImageSharp/Formats/Cur/CurFormat.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +namespace SixLabors.ImageSharp.Formats.Cur; /// /// Registers the image encoders, decoders and mime type detectors for the ICO format. diff --git a/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs b/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs similarity index 97% rename from src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs rename to src/ImageSharp/Formats/Cur/CurFrameMetadata.cs index 2d1c22776..e8f3cfe8e 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurFrameMetadata.cs +++ b/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +using SixLabors.ImageSharp.Formats.Icon; + +namespace SixLabors.ImageSharp.Formats.Cur; /// /// IcoFrameMetadata. diff --git a/src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs b/src/ImageSharp/Formats/Cur/CurMetadata.cs similarity index 89% rename from src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs rename to src/ImageSharp/Formats/Cur/CurMetadata.cs index ed3c322b4..5c3486d4a 100644 --- a/src/ImageSharp/Formats/Icon/Cur/CurMetadata.cs +++ b/src/ImageSharp/Formats/Cur/CurMetadata.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +namespace SixLabors.ImageSharp.Formats.Cur; /// /// Provides Ico specific metadata information for the image. diff --git a/src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs b/src/ImageSharp/Formats/Cur/MetadataExtensions.cs similarity index 97% rename from src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs rename to src/ImageSharp/Formats/Cur/MetadataExtensions.cs index 400ece648..2b410a0f9 100644 --- a/src/ImageSharp/Formats/Icon/Cur/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Cur/MetadataExtensions.cs @@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis; using SixLabors.ImageSharp.Metadata; -namespace SixLabors.ImageSharp.Formats.Icon.Cur; +namespace SixLabors.ImageSharp.Formats.Cur; /// /// Extension methods for the type. diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs b/src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs similarity index 88% rename from src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs rename to src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs index 7074189c7..b27d91465 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoConfigurationModule.cs +++ b/src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +using SixLabors.ImageSharp.Formats.Icon; + +namespace SixLabors.ImageSharp.Formats.Ico; /// /// Registers the image encoders, decoders and mime type detectors for the Ico format. diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs b/src/ImageSharp/Formats/Ico/IcoConstants.cs similarity index 95% rename from src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs rename to src/ImageSharp/Formats/Ico/IcoConstants.cs index 345711705..0b963a431 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoConstants.cs +++ b/src/ImageSharp/Formats/Ico/IcoConstants.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +namespace SixLabors.ImageSharp.Formats.Ico; /// /// Defines constants relating to ICOs diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs b/src/ImageSharp/Formats/Ico/IcoDecoder.cs similarity index 96% rename from src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs rename to src/ImageSharp/Formats/Ico/IcoDecoder.cs index 5d6137920..a0c8a3075 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoDecoder.cs +++ b/src/ImageSharp/Formats/Ico/IcoDecoder.cs @@ -3,7 +3,7 @@ using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +namespace SixLabors.ImageSharp.Formats.Ico; /// /// Decoder for generating an image out of a ico encoded stream. diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs similarity index 84% rename from src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs rename to src/ImageSharp/Formats/Ico/IcoDecoderCore.cs index c7841ce39..859340fb4 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs @@ -1,11 +1,10 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Icon; using SixLabors.ImageSharp.Metadata; -// TODO: flatten namespace. -// namespace SixLabors.ImageSharp.Formats.Ico; -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +namespace SixLabors.ImageSharp.Formats.Ico; internal sealed class IcoDecoderCore : IconDecoderCore { diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs b/src/ImageSharp/Formats/Ico/IcoFormat.cs similarity index 95% rename from src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs rename to src/ImageSharp/Formats/Ico/IcoFormat.cs index 27b0525bf..5f89ab7f2 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoFormat.cs +++ b/src/ImageSharp/Formats/Ico/IcoFormat.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +namespace SixLabors.ImageSharp.Formats.Ico; /// /// Registers the image encoders, decoders and mime type detectors for the ICO format. diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs similarity index 97% rename from src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs rename to src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs index 82e3c1db3..2a275d1a1 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoFrameMetadata.cs +++ b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +using SixLabors.ImageSharp.Formats.Icon; + +namespace SixLabors.ImageSharp.Formats.Ico; /// /// IcoFrameMetadata. TODO: Remove base class and merge into this class. diff --git a/src/ImageSharp/Formats/Icon/Ico/IcoMetadata.cs b/src/ImageSharp/Formats/Ico/IcoMetadata.cs similarity index 89% rename from src/ImageSharp/Formats/Icon/Ico/IcoMetadata.cs rename to src/ImageSharp/Formats/Ico/IcoMetadata.cs index b227d0bd6..f165bf916 100644 --- a/src/ImageSharp/Formats/Icon/Ico/IcoMetadata.cs +++ b/src/ImageSharp/Formats/Ico/IcoMetadata.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +namespace SixLabors.ImageSharp.Formats.Ico; /// /// Provides Ico specific metadata information for the image. diff --git a/src/ImageSharp/Formats/Icon/Ico/MetadataExtensions.cs b/src/ImageSharp/Formats/Ico/MetadataExtensions.cs similarity index 97% rename from src/ImageSharp/Formats/Icon/Ico/MetadataExtensions.cs rename to src/ImageSharp/Formats/Ico/MetadataExtensions.cs index fb5b4afe7..ba21d0abc 100644 --- a/src/ImageSharp/Formats/Icon/Ico/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Ico/MetadataExtensions.cs @@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis; using SixLabors.ImageSharp.Metadata; -namespace SixLabors.ImageSharp.Formats.Icon.Ico; +namespace SixLabors.ImageSharp.Formats.Ico; /// /// Extension methods for the type. diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs index f78c04cdf..d1587d35b 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats.Icon.Cur; +using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Cur; @@ -19,8 +19,6 @@ public class CurDecoderTests image.DebugSaveMultiFrame(provider, extension: "png"); - image.DebugSaveMultiFrame(provider, extension: "png"); - // TODO: Assert metadata, frame count, etc } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index 86f8b003b..56378653a 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats.Icon.Ico; +using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Ico; From 794b2a6433cad5183901bd22e0c08c26126f9110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:13:58 +0800 Subject: [PATCH 57/79] Refactor IconImageFormatDetector. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/IconImageFormatDetector.cs | 59 +++++++++++++++---- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs index aff8dfe0a..4fceef3b8 100644 --- a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs @@ -2,7 +2,6 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Icon; @@ -12,22 +11,58 @@ namespace SixLabors.ImageSharp.Formats.Icon; public class IconImageFormatDetector : IImageFormatDetector { /// - public int HeaderSize { get; } = 4; + public int HeaderSize { get; } = IconDir.Size + IconDirEntry.Size; /// public bool TryDetectFormat(ReadOnlySpan header, [NotNullWhen(true)] out IImageFormat? format) { - switch (MemoryMarshal.Cast(header)[0]) + format = this.IsSupportedFileFormat(header) switch { - case Ico.IcoConstants.FileHeader: - format = Ico.IcoFormat.Instance; - return true; - case Cur.CurConstants.FileHeader: - format = Cur.CurFormat.Instance; - return true; - default: - format = default; - return false; + true => Ico.IcoFormat.Instance, + false => Cur.CurFormat.Instance, + null => default + }; + + return format is not null; + } + + private bool? IsSupportedFileFormat(ReadOnlySpan header) + { + // There are no magic bytes in the first few bytes of a tga file, + // so we try to figure out if its a valid tga by checking for valid tga header bytes. + if (header.Length < this.HeaderSize) + { + return null; + } + + IconDir dir = IconDir.Parse(header); + if (dir is not { Reserved: 0 } // Should be 0. + or not { Type: IconFileType.ICO or IconFileType.CUR } // Unknown Type. + or { Count: 0 }) // Should not be 0. + { + return null; + } + + IconDirEntry entry = IconDirEntry.Parse(header[IconDir.Size..]); + if (entry is not { Reserved: 0 } // Should be 0. + or { BytesInRes: 0 } // Should not be 0. + || entry.ImageOffset < IconDir.Size + (dir.Count * IconDirEntry.Size)) + { + return null; + } + + if (dir.Type is IconFileType.ICO) + { + if (entry is not { BitCount: 1 or 4 or 8 or 16 or 24 or 32 } or not { Planes: 0 or 1 }) + { + return null; + } + + return true; + } + else + { + return false; } } } From 746e742183e671d83c42219a69ff9ea578daeeb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 14:32:56 +0800 Subject: [PATCH 58/79] Fixed errors and warnings --- src/ImageSharp/Formats/Cur/CurDecoderCore.cs | 7 +------ src/ImageSharp/Formats/Ico/IcoDecoderCore.cs | 7 +------ .../Formats/Icon/IconDecoderCore.cs | 20 +++++++++---------- src/ImageSharp/Formats/Icon/IconDir.cs | 15 ++++---------- 4 files changed, 16 insertions(+), 33 deletions(-) diff --git a/src/ImageSharp/Formats/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs index 4c0224652..538f9a2c6 100644 --- a/src/ImageSharp/Formats/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs @@ -6,13 +6,8 @@ using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp.Formats.Cur; -internal sealed class CurDecoderCore : IconDecoderCore +internal sealed class CurDecoderCore(DecoderOptions options) : IconDecoderCore(options) { - public CurDecoderCore(DecoderOptions options) - : base(options) - { - } - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, Bmp.BmpBitsPerPixel bitsPerPixel) { CurFrameMetadata curFrameMetadata = metadata.GetCurMetadata(); diff --git a/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs index 859340fb4..78cb0d961 100644 --- a/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs @@ -6,13 +6,8 @@ using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp.Formats.Ico; -internal sealed class IcoDecoderCore : IconDecoderCore +internal sealed class IcoDecoderCore(DecoderOptions options) : IconDecoderCore(options) { - public IcoDecoderCore(DecoderOptions options) - : base(options) - { - } - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, Bmp.BmpBitsPerPixel bitsPerPixel) { IcoFrameMetadata icoFrameMetadata = metadata.GetIcoMetadata(); diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 6af9553cc..983d4c346 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.IO; @@ -10,19 +9,17 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Icon; -internal abstract class IconDecoderCore : IImageDecoderInternals +internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderInternals { private IconDir fileHeader; - public IconDecoderCore(DecoderOptions options) => this.Options = options; - - public DecoderOptions Options { get; } + public DecoderOptions Options { get; } = options; public Size Dimensions { get; private set; } protected IconDir FileHeader { get => this.fileHeader; private set => this.fileHeader = value; } - protected IconDirEntry[] Entries { get; private set; } = Array.Empty(); + protected IconDirEntry[] Entries { get; private set; } = []; public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel @@ -52,7 +49,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } // Reset the stream position. - stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); + _ = stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); @@ -86,7 +83,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } // Bmp does not contain frame specific metadata. - target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngFrameMetadata()); + target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngMetadata()); } else { @@ -137,7 +134,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } // Reset the stream position. - stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); + _ = stream.Seek(-PngConstants.HeaderBytes.Length, SeekOrigin.Current); bool isPng = flag.SequenceEqual(PngConstants.HeaderBytes); @@ -210,7 +207,10 @@ internal abstract class IconDecoderCore : IImageDecoderInternals { if (isPng) { - return new PngDecoderCore(this.Options); + return new PngDecoderCore(new() + { + GeneralOptions = this.Options, + }); } else { diff --git a/src/ImageSharp/Formats/Icon/IconDir.cs b/src/ImageSharp/Formats/Icon/IconDir.cs index 53b87bc9f..390a4de65 100644 --- a/src/ImageSharp/Formats/Icon/IconDir.cs +++ b/src/ImageSharp/Formats/Icon/IconDir.cs @@ -6,12 +6,12 @@ using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Icon; [StructLayout(LayoutKind.Sequential, Pack = 1, Size = Size)] -internal struct IconDir +internal struct IconDir(ushort reserved, IconFileType type, ushort count) { public const int Size = 3 * sizeof(ushort); - public ushort Reserved; - public IconFileType Type; - public ushort Count; + public ushort Reserved = reserved; + public IconFileType Type = type; + public ushort Count = count; public IconDir(IconFileType type) : this(type, 0) @@ -23,13 +23,6 @@ internal struct IconDir { } - public IconDir(ushort reserved, IconFileType type, ushort count) - { - this.Reserved = reserved; - this.Type = type; - this.Count = count; - } - public static IconDir Parse(in ReadOnlySpan data) => MemoryMarshal.Cast(data)[0]; } From dbd178392f21d3cf36d2bb2129193453acb58fb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 17:34:38 +0800 Subject: [PATCH 59/79] Add encoders that cannot be used. --- src/ImageSharp/Formats/Bmp/BmpEncoder.cs | 9 +++ src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 81 ++++++++++++++++++- .../Formats/Cur/CurConfigurationModule.cs | 3 +- src/ImageSharp/Formats/Cur/CurEncoder.cs | 17 ++++ src/ImageSharp/Formats/Cur/CurEncoderCore.cs | 19 +++++ .../Formats/Ico/IcoConfigurationModule.cs | 3 +- src/ImageSharp/Formats/Ico/IcoEncoder.cs | 17 ++++ src/ImageSharp/Formats/Ico/IcoEncoderCore.cs | 19 +++++ src/ImageSharp/Formats/Icon/IconAssert.cs | 14 ++++ src/ImageSharp/Formats/Icon/IconDir.cs | 3 + src/ImageSharp/Formats/Icon/IconDirEntry.cs | 3 + .../Formats/Icon/IconEncoderCore.cs | 69 ++++++++++++++++ src/ImageSharp/Metadata/ImageMetadata.cs | 1 + .../Formats/Icon/Cur/CurEncoderTests.cs | 32 ++++++++ .../Formats/Icon/Ico/IcoEncoderTests.cs | 32 ++++++++ 15 files changed, 316 insertions(+), 6 deletions(-) create mode 100644 src/ImageSharp/Formats/Cur/CurEncoder.cs create mode 100644 src/ImageSharp/Formats/Cur/CurEncoderCore.cs create mode 100644 src/ImageSharp/Formats/Ico/IcoEncoder.cs create mode 100644 src/ImageSharp/Formats/Ico/IcoEncoderCore.cs create mode 100644 src/ImageSharp/Formats/Icon/IconEncoderCore.cs create mode 100644 tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs create mode 100644 tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs index 0081f6a1a..0be243f9a 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs @@ -29,6 +29,15 @@ public sealed class BmpEncoder : QuantizingImageEncoder /// public bool SupportTransparency { get; init; } + /// + internal bool ProcessedAlphaMask { get; init; } + + /// + internal bool SkipFileHeader { get; init; } + + /// + internal bool UseDoubleHeight { get; init; } + /// protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 076d1adf0..b888fa400 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -3,6 +3,7 @@ using System.Buffers; using System.Buffers.Binary; +using System.IO; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Common.Helpers; @@ -11,6 +12,7 @@ using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; +using static System.Net.Mime.MediaTypeNames; namespace SixLabors.ImageSharp.Formats.Bmp; @@ -92,6 +94,15 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// private readonly IPixelSamplingStrategy pixelSamplingStrategy; + /// + private readonly bool processedAlphaMask; + + /// + private readonly bool skipFileHeader; + + /// + private readonly bool isDoubleHeight; + /// /// Initializes a new instance of the class. /// @@ -104,6 +115,9 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals this.quantizer = encoder.Quantizer ?? KnownQuantizers.Octree; this.pixelSamplingStrategy = encoder.PixelSamplingStrategy; this.infoHeaderType = encoder.SupportTransparency ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion3; + this.processedAlphaMask = encoder.ProcessedAlphaMask; + this.skipFileHeader = encoder.SkipFileHeader; + this.isDoubleHeight = encoder.UseDoubleHeight; } /// @@ -154,11 +168,23 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals _ => BmpInfoHeader.SizeV3 }; - BmpInfoHeader infoHeader = this.CreateBmpInfoHeader(image.Width, image.Height, infoHeaderSize, bpp, bytesPerLine, metadata, iccProfileData); + // for ico/cur encoder. + int height = image.Height; + if (this.isDoubleHeight) + { + height <<= 1; + } + + BmpInfoHeader infoHeader = this.CreateBmpInfoHeader(image.Width, height, infoHeaderSize, bpp, bytesPerLine, metadata, iccProfileData); Span buffer = stackalloc byte[infoHeaderSize]; - WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer); + // for ico/cur encoder. + if (!this.skipFileHeader) + { + WriteBitmapFileHeader(stream, infoHeaderSize, colorPaletteSize, iccProfileSize, infoHeader, buffer); + } + this.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize); this.WriteImage(configuration, stream, image); WriteColorProfile(stream, iccProfileData, buffer); @@ -455,6 +481,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals { this.Write8BitColor(configuration, stream, image, colorPalette); } + + if (this.processedAlphaMask) + { + ProcessedAlphaMask(stream, image); + } } /// @@ -572,6 +603,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals stream.WriteByte(0); } } + + if (this.processedAlphaMask) + { + ProcessedAlphaMask(stream, image); + } } /// @@ -629,6 +665,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals stream.WriteByte(0); } } + + if (this.processedAlphaMask) + { + ProcessedAlphaMask(stream, image); + } } /// @@ -679,6 +720,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals stream.WriteByte(0); } } + + if (this.processedAlphaMask) + { + ProcessedAlphaMask(stream, image); + } } /// @@ -722,4 +768,35 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals stream.WriteByte(indices); } + + private static void ProcessedAlphaMask(Stream stream, Image image) + where TPixel : unmanaged, IPixel + { + Rgba32 rgba = default; + int arrayWidth = image.Width / 8; + int padding = arrayWidth % 4; + if (padding is not 0) + { + padding = 4 - padding; + } + + Span mask = stackalloc byte[arrayWidth]; + for (int y = image.Height - 1; y >= 0; y--) + { + mask.Clear(); + for (int x = 0; x < image.Width; x++) + { + int bit = x % 8; + int i = x / 8; + TPixel pixel = image[x, y]; + pixel.ToRgba32(ref rgba); + if (rgba.A is not 0) + { + mask[i] &= unchecked((byte)(0b10000000 >> bit)); + } + } + + stream.Write(mask); + } + } } diff --git a/src/ImageSharp/Formats/Cur/CurConfigurationModule.cs b/src/ImageSharp/Formats/Cur/CurConfigurationModule.cs index 1c7db4bab..879b3f112 100644 --- a/src/ImageSharp/Formats/Cur/CurConfigurationModule.cs +++ b/src/ImageSharp/Formats/Cur/CurConfigurationModule.cs @@ -13,8 +13,7 @@ public sealed class CurConfigurationModule : IImageFormatConfigurationModule /// public void Configure(Configuration configuration) { - // TODO: CurEncoder - // configuration.ImageFormatsManager.SetEncoder(CurFormat.Instance, new CurEncoder()); + configuration.ImageFormatsManager.SetEncoder(CurFormat.Instance, new CurEncoder()); configuration.ImageFormatsManager.SetDecoder(CurFormat.Instance, CurDecoder.Instance); configuration.ImageFormatsManager.AddImageFormatDetector(new IconImageFormatDetector()); } diff --git a/src/ImageSharp/Formats/Cur/CurEncoder.cs b/src/ImageSharp/Formats/Cur/CurEncoder.cs new file mode 100644 index 000000000..d237fe7d0 --- /dev/null +++ b/src/ImageSharp/Formats/Cur/CurEncoder.cs @@ -0,0 +1,17 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Cur; + +/// +/// Image encoder for writing an image to a stream as a Windows Cursor. +/// +public sealed class CurEncoder : QuantizingImageEncoder +{ + /// + protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) + { + CurEncoderCore encoderCore = new(); + encoderCore.Encode(image, stream, cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs new file mode 100644 index 000000000..0cf8c97a5 --- /dev/null +++ b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Icon; + +namespace SixLabors.ImageSharp.Formats.Cur; + +internal sealed class CurEncoderCore : IconEncoderCore +{ + protected override void GetHeader(in Image image) + { + this.FileHeader = new(IconFileType.ICO, (ushort)image.Frames.Count); + this.Entries = image.Frames.Select(i => + { + CurFrameMetadata metadata = i.Metadata.GetCurMetadata(); + return metadata.ToIconDirEntry(); + }).ToArray(); + } +} diff --git a/src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs b/src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs index b27d91465..224aaa31e 100644 --- a/src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs +++ b/src/ImageSharp/Formats/Ico/IcoConfigurationModule.cs @@ -13,8 +13,7 @@ public sealed class IcoConfigurationModule : IImageFormatConfigurationModule /// public void Configure(Configuration configuration) { - // TODO: IcoEncoder - // configuration.ImageFormatsManager.SetEncoder(IcoFormat.Instance, new IcoEncoder()); + configuration.ImageFormatsManager.SetEncoder(IcoFormat.Instance, new IcoEncoder()); configuration.ImageFormatsManager.SetDecoder(IcoFormat.Instance, IcoDecoder.Instance); configuration.ImageFormatsManager.AddImageFormatDetector(new IconImageFormatDetector()); } diff --git a/src/ImageSharp/Formats/Ico/IcoEncoder.cs b/src/ImageSharp/Formats/Ico/IcoEncoder.cs new file mode 100644 index 000000000..0668a7e23 --- /dev/null +++ b/src/ImageSharp/Formats/Ico/IcoEncoder.cs @@ -0,0 +1,17 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Ico; + +/// +/// Image encoder for writing an image to a stream as a Windows Icon. +/// +public sealed class IcoEncoder : QuantizingImageEncoder +{ + /// + protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) + { + IcoEncoderCore encoderCore = new(); + encoderCore.Encode(image, stream, cancellationToken); + } +} diff --git a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs new file mode 100644 index 000000000..6c2f3abe4 --- /dev/null +++ b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs @@ -0,0 +1,19 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Icon; + +namespace SixLabors.ImageSharp.Formats.Ico; + +internal sealed class IcoEncoderCore : IconEncoderCore +{ + protected override void GetHeader(in Image image) + { + this.FileHeader = new(IconFileType.ICO, (ushort)image.Frames.Count); + this.Entries = image.Frames.Select(i => + { + IcoFrameMetadata metadata = i.Metadata.GetIcoMetadata(); + return metadata.ToIconDirEntry(); + }).ToArray(); + } +} diff --git a/src/ImageSharp/Formats/Icon/IconAssert.cs b/src/ImageSharp/Formats/Icon/IconAssert.cs index 04a9527b9..547b6a6eb 100644 --- a/src/ImageSharp/Formats/Icon/IconAssert.cs +++ b/src/ImageSharp/Formats/Icon/IconAssert.cs @@ -32,4 +32,18 @@ internal class IconAssert return v; } + + internal static byte Is1ByteSize(int i) + { + if (i is 256) + { + return 0; + } + else if (i > byte.MaxValue) + { + throw new FormatException("Image size Too Large."); + } + + return (byte)i; + } } diff --git a/src/ImageSharp/Formats/Icon/IconDir.cs b/src/ImageSharp/Formats/Icon/IconDir.cs index 390a4de65..aa583ee1e 100644 --- a/src/ImageSharp/Formats/Icon/IconDir.cs +++ b/src/ImageSharp/Formats/Icon/IconDir.cs @@ -25,4 +25,7 @@ internal struct IconDir(ushort reserved, IconFileType type, ushort count) public static IconDir Parse(in ReadOnlySpan data) => MemoryMarshal.Cast(data)[0]; + + public unsafe void WriteTo(in Stream stream) + => stream.Write(MemoryMarshal.Cast([this])); } diff --git a/src/ImageSharp/Formats/Icon/IconDirEntry.cs b/src/ImageSharp/Formats/Icon/IconDirEntry.cs index edd778f7e..7a8e09e37 100644 --- a/src/ImageSharp/Formats/Icon/IconDirEntry.cs +++ b/src/ImageSharp/Formats/Icon/IconDirEntry.cs @@ -28,4 +28,7 @@ internal struct IconDirEntry public static IconDirEntry Parse(in ReadOnlySpan data) => MemoryMarshal.Cast(data)[0]; + + public unsafe void WriteTo(in Stream stream) + => stream.Write(MemoryMarshal.Cast([this])); } diff --git a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs new file mode 100644 index 000000000..05a657b51 --- /dev/null +++ b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs @@ -0,0 +1,69 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Icon; + +internal abstract class IconEncoderCore : IImageEncoderInternals +{ + protected IconDir FileHeader { get; set; } + + protected IconDirEntry[]? Entries { get; set; } + + public void Encode( + Image image, + Stream stream, + CancellationToken cancellationToken) + where TPixel : unmanaged, IPixel + { + Guard.NotNull(image, nameof(image)); + Guard.NotNull(stream, nameof(stream)); + + IconAssert.CanSeek(stream); + + // Stream may not at 0. + long basePosition = stream.Position; + this.GetHeader(image); + + int dataOffset = IconDir.Size + (IconDirEntry.Size * this.Entries.Length); + _ = stream.Seek(dataOffset, SeekOrigin.Current); + + for (int i = 0; i < image.Frames.Count; i++) + { + ImageFrame frame = image.Frames[i]; + this.Entries[i].ImageOffset = (uint)stream.Position; + Image img = new(Configuration.Default, frame.PixelBuffer, new()); + + // Note: this encoder are not supported PNG Data. + BmpEncoder encoder = new() + { + ProcessedAlphaMask = true, + UseDoubleHeight = true, + SkipFileHeader = true, + SupportTransparency = false, + BitsPerPixel = this.Entries[i].BitCount is 0 + ? BmpBitsPerPixel.Pixel8 + : (BmpBitsPerPixel?)this.Entries[i].BitCount + }; + + encoder.Encode(img, stream); + this.Entries[i].BytesInRes = this.Entries[i].ImageOffset - (uint)stream.Position; + } + + long endPosition = stream.Position; + _ = stream.Seek(basePosition, SeekOrigin.Begin); + this.FileHeader.WriteTo(stream); + foreach (IconDirEntry entry in this.Entries) + { + entry.WriteTo(stream); + } + + _ = stream.Seek(endPosition, SeekOrigin.Begin); + } + + [MemberNotNull(nameof(Entries))] + protected abstract void GetHeader(in Image image); +} diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs index d9aba6631..e811cc1f7 100644 --- a/src/ImageSharp/Metadata/ImageMetadata.cs +++ b/src/ImageSharp/Metadata/ImageMetadata.cs @@ -215,6 +215,7 @@ public sealed class ImageMetadata : IDeepCloneable metadata = default; return false; } + internal void SetFormatMetadata(IImageFormat key, TFormatMetadata value) where TFormatMetadata : class, IDeepCloneable => this.formatMetadata[key] = value; diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs new file mode 100644 index 000000000..9908786f1 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Cur; +using SixLabors.ImageSharp.PixelFormats; +using static SixLabors.ImageSharp.Tests.TestImages.Cur; + +namespace SixLabors.ImageSharp.Tests.Formats.Icon.Cur; + +[Trait("Format", "Cur")] +public class CurEncoderTests +{ + private static CurEncoder CurEncoder => new(); + + public static readonly TheoryData Files = new() + { + { WindowsMouse }, + }; + + [Theory] + [MemberData(nameof(Files))] + public void Encode(string imagePath) + { + TestFile testFile = TestFile.Create(imagePath); + using Image input = testFile.CreateRgba32Image(); + using MemoryStream memStream = new(); + input.Save(memStream, CurEncoder); + + memStream.Seek(0, SeekOrigin.Begin); + CurDecoder.Instance.Decode(new(), memStream); + } +} diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs new file mode 100644 index 000000000..9a239bdd4 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Ico; +using SixLabors.ImageSharp.PixelFormats; +using static SixLabors.ImageSharp.Tests.TestImages.Ico; + +namespace SixLabors.ImageSharp.Tests.Formats.Icon.Ico; + +[Trait("Format", "Icon")] +public class IcoEncoderTests +{ + private static IcoEncoder CurEncoder => new(); + + public static readonly TheoryData Files = new() + { + { Flutter }, + }; + + [Theory] + [MemberData(nameof(Files))] + public void Encode(string imagePath) + { + TestFile testFile = TestFile.Create(imagePath); + using Image input = testFile.CreateRgba32Image(); + using MemoryStream memStream = new(); + input.Save(memStream, CurEncoder); + + memStream.Seek(0, SeekOrigin.Begin); + IcoDecoder.Instance.Decode(new(), memStream); + } +} From 54e87ab63ace1daa546f0476408fb84ab1557967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 19:43:12 +0800 Subject: [PATCH 60/79] Remove a comment --- src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs index 4fceef3b8..b66b6c79f 100644 --- a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs @@ -38,7 +38,7 @@ public class IconImageFormatDetector : IImageFormatDetector IconDir dir = IconDir.Parse(header); if (dir is not { Reserved: 0 } // Should be 0. or not { Type: IconFileType.ICO or IconFileType.CUR } // Unknown Type. - or { Count: 0 }) // Should not be 0. + or { Count: 0 }) { return null; } From 21b1e71062915603e16792b88e5cb528d5444d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 21:47:27 +0800 Subject: [PATCH 61/79] Encoder Worked! but AlphaMask have some problems --- src/ImageSharp/Formats/Cur/CurEncoderCore.cs | 11 +-- .../Formats/Cur/MetadataExtensions.cs | 5 +- src/ImageSharp/Formats/Ico/IcoEncoderCore.cs | 11 +-- .../Formats/Ico/IcoFrameMetadata.cs | 3 +- .../Formats/Ico/MetadataExtensions.cs | 5 +- .../Formats/Icon/IconEncoderCore.cs | 97 ++++++++++++++----- 6 files changed, 84 insertions(+), 48 deletions(-) diff --git a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs index 0cf8c97a5..3a7288b71 100644 --- a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs @@ -5,15 +5,6 @@ using SixLabors.ImageSharp.Formats.Icon; namespace SixLabors.ImageSharp.Formats.Cur; -internal sealed class CurEncoderCore : IconEncoderCore +internal sealed class CurEncoderCore() : IconEncoderCore(IconFileType.CUR) { - protected override void GetHeader(in Image image) - { - this.FileHeader = new(IconFileType.ICO, (ushort)image.Frames.Count); - this.Entries = image.Frames.Select(i => - { - CurFrameMetadata metadata = i.Metadata.GetCurMetadata(); - return metadata.ToIconDirEntry(); - }).ToArray(); - } } diff --git a/src/ImageSharp/Formats/Cur/MetadataExtensions.cs b/src/ImageSharp/Formats/Cur/MetadataExtensions.cs index 2b410a0f9..6394c564b 100644 --- a/src/ImageSharp/Formats/Cur/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Cur/MetadataExtensions.cs @@ -2,14 +2,15 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.Metadata; -namespace SixLabors.ImageSharp.Formats.Cur; +namespace SixLabors.ImageSharp; /// /// Extension methods for the type. /// -public static class MetadataExtensions +public static partial class MetadataExtensions { /// /// Gets the Icon format specific metadata for the image. diff --git a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs index 6c2f3abe4..12ced58fd 100644 --- a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs @@ -5,15 +5,6 @@ using SixLabors.ImageSharp.Formats.Icon; namespace SixLabors.ImageSharp.Formats.Ico; -internal sealed class IcoEncoderCore : IconEncoderCore +internal sealed class IcoEncoderCore() : IconEncoderCore(IconFileType.ICO) { - protected override void GetHeader(in Image image) - { - this.FileHeader = new(IconFileType.ICO, (ushort)image.Frames.Count); - this.Entries = image.Frames.Select(i => - { - IcoFrameMetadata metadata = i.Metadata.GetIcoMetadata(); - return metadata.ToIconDirEntry(); - }).ToArray(); - } } diff --git a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs index 2a275d1a1..8d7eb17b5 100644 --- a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs +++ b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs @@ -88,7 +88,8 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable BitCount = this.Compression switch { IconFrameCompression.Bmp => (ushort)this.BitsPerPixel, - _ => 0, + IconFrameCompression.Png => 32, + _ => throw new NotSupportedException($"Value: {this.Compression}"), }, }; } diff --git a/src/ImageSharp/Formats/Ico/MetadataExtensions.cs b/src/ImageSharp/Formats/Ico/MetadataExtensions.cs index ba21d0abc..497375f99 100644 --- a/src/ImageSharp/Formats/Ico/MetadataExtensions.cs +++ b/src/ImageSharp/Formats/Ico/MetadataExtensions.cs @@ -2,14 +2,15 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.Metadata; -namespace SixLabors.ImageSharp.Formats.Ico; +namespace SixLabors.ImageSharp; /// /// Extension methods for the type. /// -public static class MetadataExtensions +public static partial class MetadataExtensions { /// /// Gets the Ico format specific metadata for the image. diff --git a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs index 05a657b51..b4d563d4b 100644 --- a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs @@ -2,16 +2,18 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; -using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.Formats.Cur; +using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Icon; -internal abstract class IconEncoderCore : IImageEncoderInternals +internal abstract class IconEncoderCore(IconFileType iconFileType) + : IImageEncoderInternals { - protected IconDir FileHeader { get; set; } + private IconDir fileHeader; - protected IconDirEntry[]? Entries { get; set; } + private IconFrameMetadata[]? entries; public void Encode( Image image, @@ -26,44 +28,93 @@ internal abstract class IconEncoderCore : IImageEncoderInternals // Stream may not at 0. long basePosition = stream.Position; - this.GetHeader(image); + this.InitHeader(image); - int dataOffset = IconDir.Size + (IconDirEntry.Size * this.Entries.Length); + int dataOffset = IconDir.Size + (IconDirEntry.Size * this.entries.Length); _ = stream.Seek(dataOffset, SeekOrigin.Current); for (int i = 0; i < image.Frames.Count; i++) { ImageFrame frame = image.Frames[i]; - this.Entries[i].ImageOffset = (uint)stream.Position; - Image img = new(Configuration.Default, frame.PixelBuffer, new()); + int width = this.entries[i].Entry.Width; + if (width is 0) + { + width = frame.Width; + } + + int height = this.entries[i].Entry.Height; + if (height is 0) + { + height = frame.Height; + } + + this.entries[i].Entry.ImageOffset = (uint)stream.Position; - // Note: this encoder are not supported PNG Data. - BmpEncoder encoder = new() + Image img = new(width, height); + for (int y = 0; y < height; y++) { - ProcessedAlphaMask = true, - UseDoubleHeight = true, - SkipFileHeader = true, - SupportTransparency = false, - BitsPerPixel = this.Entries[i].BitCount is 0 - ? BmpBitsPerPixel.Pixel8 - : (BmpBitsPerPixel?)this.Entries[i].BitCount + frame.PixelBuffer.DangerousGetRowSpan(y)[..width].CopyTo(img.GetRootFramePixelBuffer().DangerousGetRowSpan(y)); + } + + QuantizingImageEncoder encoder = this.entries[i].Compression switch + { + IconFrameCompression.Bmp => new Bmp.BmpEncoder() + { + ProcessedAlphaMask = true, + UseDoubleHeight = true, + SkipFileHeader = true, + SupportTransparency = false, + BitsPerPixel = iconFileType is IconFileType.ICO + ? (Bmp.BmpBitsPerPixel?)this.entries[i].Entry.BitCount + : Bmp.BmpBitsPerPixel.Pixel24 // TODO: Here you need to switch to selecting the corresponding value according to the size of the image + }, + IconFrameCompression.Png => new Png.PngEncoder(), + _ => throw new NotSupportedException(), }; encoder.Encode(img, stream); - this.Entries[i].BytesInRes = this.Entries[i].ImageOffset - (uint)stream.Position; + this.entries[i].Entry.BytesInRes = (uint)stream.Position - this.entries[i].Entry.ImageOffset; } long endPosition = stream.Position; _ = stream.Seek(basePosition, SeekOrigin.Begin); - this.FileHeader.WriteTo(stream); - foreach (IconDirEntry entry in this.Entries) + this.fileHeader.WriteTo(stream); + foreach (IconFrameMetadata frame in this.entries) { - entry.WriteTo(stream); + frame.Entry.WriteTo(stream); } _ = stream.Seek(endPosition, SeekOrigin.Begin); } - [MemberNotNull(nameof(Entries))] - protected abstract void GetHeader(in Image image); + [MemberNotNull(nameof(entries))] + private void InitHeader(in Image image) + { + this.fileHeader = new(iconFileType, (ushort)image.Frames.Count); + this.entries = iconFileType switch + { + IconFileType.ICO => + image.Frames.Select(i => + { + IcoFrameMetadata metadata = i.Metadata.GetIcoMetadata(); + return new IconFrameMetadata(metadata.Compression, metadata.ToIconDirEntry()); + }).ToArray(), + IconFileType.CUR => + image.Frames.Select(i => + { + CurFrameMetadata metadata = i.Metadata.GetCurMetadata(); + return new IconFrameMetadata(metadata.Compression, metadata.ToIconDirEntry()); + }).ToArray(), + _ => throw new NotSupportedException(), + }; + } + + internal sealed class IconFrameMetadata(IconFrameCompression compression, IconDirEntry iconDirEntry) + { + private IconDirEntry iconDirEntry = iconDirEntry; + + public IconFrameCompression Compression { get; set; } = compression; + + public ref IconDirEntry Entry => ref this.iconDirEntry; + } } From e252a6f223120357fe00cc9c3aa842d250b131ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 21:48:34 +0800 Subject: [PATCH 62/79] Using Seek requires consideration of whether this stream allows Seek and the case where the stream start position is not 0. --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index b888fa400..dcccc8363 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -133,6 +133,9 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); + // Stream may not at 0. + long basePosition = stream.Position; + Configuration configuration = image.Configuration; ImageMetadata metadata = image.Metadata; BmpMetadata bmpMetadata = metadata.GetBmpMetadata(); @@ -187,7 +190,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals this.WriteBitmapInfoHeader(stream, infoHeader, buffer, infoHeaderSize); this.WriteImage(configuration, stream, image); - WriteColorProfile(stream, iccProfileData, buffer); + WriteColorProfile(stream, iccProfileData, buffer, basePosition); stream.Flush(); } @@ -271,16 +274,20 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// The stream to write to. /// The color profile data. /// The buffer. - private static void WriteColorProfile(Stream stream, byte[]? iccProfileData, Span buffer) + /// The Stream may not be start with 0. + private static void WriteColorProfile(Stream stream, byte[]? iccProfileData, Span buffer, long basePosition) { if (iccProfileData != null) { // The offset, in bytes, from the beginning of the BITMAPV5HEADER structure to the start of the profile data. int streamPositionAfterImageData = (int)stream.Position - BmpFileHeader.Size; stream.Write(iccProfileData); + long position = stream.Position; // Storage Position BinaryPrimitives.WriteInt32LittleEndian(buffer, streamPositionAfterImageData); - stream.Position = BmpFileHeader.Size + 112; + _ = stream.Seek(basePosition, SeekOrigin.Begin); + _ = stream.Seek(BmpFileHeader.Size + 112, SeekOrigin.Current); stream.Write(buffer[..4]); + _ = stream.Seek(position, SeekOrigin.Begin); // Reset Position } } From fae15ae88aa1b8131076159b199f51c1d32ac70e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 22:51:52 +0800 Subject: [PATCH 63/79] Fixed AlphaMask --- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 55 +++++++++----------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index dcccc8363..fe1c9e0be 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -3,16 +3,13 @@ using System.Buffers; using System.Buffers.Binary; -using System.IO; using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; -using static System.Net.Mime.MediaTypeNames; namespace SixLabors.ImageSharp.Formats.Bmp; @@ -380,6 +377,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals this.Write1BitPixelData(configuration, stream, image); break; } + + if (this.processedAlphaMask) + { + ProcessedAlphaMask(stream, image); + } } private IMemoryOwner AllocateRow(int width, int bytesPerPixel) @@ -488,11 +490,6 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals { this.Write8BitColor(configuration, stream, image, colorPalette); } - - if (this.processedAlphaMask) - { - ProcessedAlphaMask(stream, image); - } } /// @@ -610,11 +607,6 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals stream.WriteByte(0); } } - - if (this.processedAlphaMask) - { - ProcessedAlphaMask(stream, image); - } } /// @@ -672,11 +664,6 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals stream.WriteByte(0); } } - - if (this.processedAlphaMask) - { - ProcessedAlphaMask(stream, image); - } } /// @@ -727,11 +714,6 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals stream.WriteByte(0); } } - - if (this.processedAlphaMask) - { - ProcessedAlphaMask(stream, image); - } } /// @@ -779,7 +761,6 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals private static void ProcessedAlphaMask(Stream stream, Image image) where TPixel : unmanaged, IPixel { - Rgba32 rgba = default; int arrayWidth = image.Width / 8; int padding = arrayWidth % 4; if (padding is not 0) @@ -791,19 +772,31 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals for (int y = image.Height - 1; y >= 0; y--) { mask.Clear(); - for (int x = 0; x < image.Width; x++) + Span row = image.GetRootFramePixelBuffer().DangerousGetRowSpan(y); + + for (int i = 0; i < arrayWidth; i++) { - int bit = x % 8; - int i = x / 8; - TPixel pixel = image[x, y]; - pixel.ToRgba32(ref rgba); - if (rgba.A is not 0) + int x = i * 8; + + for (int j = 0; j < 8; j++) { - mask[i] &= unchecked((byte)(0b10000000 >> bit)); + WriteAlphaMask(row[x + j], ref mask[i], j); } } stream.Write(mask); + stream.Skip(padding); + } + } + + private static void WriteAlphaMask(in TPixel pixel, ref byte mask, in int index) + where TPixel : unmanaged, IPixel + { + Rgba32 rgba = default; + pixel.ToRgba32(ref rgba); + if (rgba.A is 0) + { + mask |= unchecked((byte)(0b10000000 >> index)); } } } From 52f392db568bf27cbe127ae1812f6d05a9ab7df2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Thu, 14 Dec 2023 22:58:15 +0800 Subject: [PATCH 64/79] Fixed some warnings. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/IconDecoderCore.cs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 983d4c346..a502b91c9 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics.CodeAnalysis; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.IO; @@ -12,15 +13,12 @@ namespace SixLabors.ImageSharp.Formats.Icon; internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderInternals { private IconDir fileHeader; + private IconDirEntry[]? entries; public DecoderOptions Options { get; } = options; public Size Dimensions { get; private set; } - protected IconDir FileHeader { get => this.fileHeader; private set => this.fileHeader = value; } - - protected IconDirEntry[] Entries { get; private set; } = []; - public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { @@ -30,11 +28,11 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; - List<(Image Image, IconFrameCompression Compression, int Index)> decodedEntries = new(this.Entries.Length); + List<(Image Image, IconFrameCompression Compression, int Index)> decodedEntries = new(this.entries.Length); - for (int i = 0; i < this.Entries.Length; i++) + for (int i = 0; i < this.entries.Length; i++) { - ref IconDirEntry entry = ref this.Entries[i]; + ref IconDirEntry entry = ref this.entries[i]; // If we hit the end of the stream we should break. if (stream.Seek(basePosition + entry.ImageOffset, SeekOrigin.Begin) >= stream.Length) @@ -90,7 +88,7 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI bitsPerPixel = x.Image.Metadata.GetBmpMetadata().BitsPerPixel; } - this.SetFrameMetadata(target.Metadata, this.Entries[x.Index], x.Compression, bitsPerPixel); + this.SetFrameMetadata(target.Metadata, this.entries[x.Index], x.Compression, bitsPerPixel); x.Image.Dispose(); @@ -115,11 +113,11 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; ImageMetadata metadata = new(); - ImageFrameMetadata[] frames = new ImageFrameMetadata[this.FileHeader.Count]; + ImageFrameMetadata[] frames = new ImageFrameMetadata[this.fileHeader.Count]; for (int i = 0; i < frames.Length; i++) { BmpBitsPerPixel bitsPerPixel = default; - ref IconDirEntry entry = ref this.Entries[i]; + ref IconDirEntry entry = ref this.entries[i]; // If we hit the end of the stream we should break. if (stream.Seek(basePosition + entry.ImageOffset, SeekOrigin.Begin) >= stream.Length) @@ -147,7 +145,7 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI bitsPerPixel = temp.Metadata.GetBmpMetadata().BitsPerPixel; } - this.SetFrameMetadata(frames[i], this.Entries[i], isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, bitsPerPixel); + this.SetFrameMetadata(frames[i], this.entries[i], isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, bitsPerPixel); // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. @@ -159,6 +157,7 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI protected abstract void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel); + [MemberNotNull(nameof(entries))] protected void ReadHeader(Stream stream) { Span buffer = stackalloc byte[IconDirEntry.Size]; @@ -168,16 +167,16 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI this.fileHeader = IconDir.Parse(buffer); // ICONDIRENTRY - this.Entries = new IconDirEntry[this.FileHeader.Count]; - for (int i = 0; i < this.Entries.Length; i++) + this.entries = new IconDirEntry[this.fileHeader.Count]; + for (int i = 0; i < this.entries.Length; i++) { _ = IconAssert.EndOfStream(stream.Read(buffer[..IconDirEntry.Size]), IconDirEntry.Size); - this.Entries[i] = IconDirEntry.Parse(buffer); + this.entries[i] = IconDirEntry.Parse(buffer); } int width = 0; int height = 0; - foreach (IconDirEntry entry in this.Entries) + foreach (IconDirEntry entry in this.entries) { // Since Windows 95 size of an image in the ICONDIRENTRY structure might // be set to zero, which means 256 pixels. From 5be5d64882e5e85fed199cbc621bcd16734b008b Mon Sep 17 00:00:00 2001 From: Poker Date: Thu, 14 Mar 2024 12:17:44 +0800 Subject: [PATCH 65/79] fix --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 4 ++-- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 568eea00a..11c6aef29 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -898,7 +898,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals { int colorIndex = ((rowSpan[offset] >> (8 - bitsPerPixel - (shift * bitsPerPixel))) & mask) * bytesPerColorMapEntry; - image[newY, newX].FromBgr24(Unsafe.As(ref colors[colorIndex])); + image[newY, newX] = Bgra32.FromBgr24(Unsafe.As(ref colors[colorIndex])); } offset++; @@ -942,7 +942,7 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals for (int x = 0; x < width; x++) { - pixelRow[x].FromBgra32(image[newY, x]); + pixelRow[x] = TPixel.FromBgra32(image[newY, x]); } } } diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index fe1c9e0be..f84e4f9c2 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -792,8 +792,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals private static void WriteAlphaMask(in TPixel pixel, ref byte mask, in int index) where TPixel : unmanaged, IPixel { - Rgba32 rgba = default; - pixel.ToRgba32(ref rgba); + Rgba32 rgba = pixel.ToRgba32(); if (rgba.A is 0) { mask |= unchecked((byte)(0b10000000 >> index)); From af007fa1cdb0171037e4f36ef72ca7850d610cf6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 11 Jun 2024 14:38:30 +1000 Subject: [PATCH 66/79] Allow skipping bad metadata using identify --- src/ImageSharp/Formats/Gif/LzwDecoder.cs | 2 +- .../Formats/Gif/GifMetadataTests.cs | 26 ++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index aecd8e5b4..19f646b59 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -255,7 +255,7 @@ internal sealed class LzwDecoder : IDisposable // Don't attempt to decode the frame indices. // Theoretically we could determine a min code size from the length of the provided // color palette but we won't bother since the image is most likely corrupted. - GifThrowHelper.ThrowInvalidImageContentException("Gif Image does not contain a valid LZW minimum code."); + return; } int codeSize = minCodeSize + 1; diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs index fb4445cda..f9c56e05e 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -35,7 +35,7 @@ public class GifMetadataTests RepeatCount = 1, ColorTableMode = GifColorTableMode.Global, GlobalColorTable = new[] { Color.Black, Color.White }, - Comments = new List { "Foo" } + Comments = ["Foo"] }; GifMetadata clone = (GifMetadata)meta.DeepClone(); @@ -126,7 +126,7 @@ public class GifMetadataTests public async Task Identify_VerifyRatioAsync(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { TestFile testFile = TestFile.Create(imagePath); - using MemoryStream stream = new(testFile.Bytes, false); + await using MemoryStream stream = new(testFile.Bytes, false); ImageInfo image = await GifDecoder.Instance.IdentifyAsync(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); @@ -152,7 +152,7 @@ public class GifMetadataTests public async Task Decode_VerifyRatioAsync(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { TestFile testFile = TestFile.Create(imagePath); - using MemoryStream stream = new(testFile.Bytes, false); + await using MemoryStream stream = new(testFile.Bytes, false); using Image image = await GifDecoder.Instance.DecodeAsync(DecoderOptions.Default, stream); ImageMetadata meta = image.Metadata; Assert.Equal(xResolution, meta.HorizontalResolution); @@ -214,4 +214,24 @@ public class GifMetadataTests Assert.Equal(frameDelay, gifFrameMetadata.FrameDelay); Assert.Equal(disposalMethod, gifFrameMetadata.DisposalMethod); } + + [Theory] + [InlineData(TestImages.Gif.Issues.BadMaxLzwBits, 8)] + [InlineData(TestImages.Gif.Issues.Issue2012BadMinCode, 1)] + public void Identify_Frames_Bad_Lzw(string imagePath, int framesCount) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + + ImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + GifMetadata gifMetadata = imageInfo.Metadata.GetGifMetadata(); + Assert.NotNull(gifMetadata); + + Assert.Equal(framesCount, imageInfo.FrameMetadataCollection.Count); + GifFrameMetadata gifFrameMetadata = imageInfo.FrameMetadataCollection[imageInfo.FrameMetadataCollection.Count - 1].GetGifMetadata(); + + Assert.NotNull(gifFrameMetadata); + } } From 6beeba1575ed58dbde57332714c639a2d1451611 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 18 Jun 2024 21:41:08 +1000 Subject: [PATCH 67/79] Cleanup and fix issues --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 9 ++ src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 2 + src/ImageSharp/Formats/Bmp/BmpMetadata.cs | 14 +++- src/ImageSharp/Formats/Cur/CurConstants.cs | 25 ++++-- src/ImageSharp/Formats/Cur/CurDecoderCore.cs | 12 ++- src/ImageSharp/Formats/Cur/CurEncoderCore.cs | 6 +- .../Formats/Cur/CurFrameMetadata.cs | 83 ++++++++----------- src/ImageSharp/Formats/Ico/IcoConstants.cs | 11 +-- src/ImageSharp/Formats/Ico/IcoDecoderCore.cs | 12 ++- src/ImageSharp/Formats/Ico/IcoEncoder.cs | 4 +- src/ImageSharp/Formats/Ico/IcoEncoderCore.cs | 6 +- .../Formats/Ico/IcoFrameMetadata.cs | 76 +++++++---------- src/ImageSharp/Formats/Icon/IconAssert.cs | 32 ------- .../Formats/Icon/IconDecoderCore.cs | 44 ++++++---- src/ImageSharp/Formats/Icon/IconDir.cs | 16 +++- src/ImageSharp/Formats/Icon/IconDirEntry.cs | 28 ++++++- .../Formats/Icon/IconEncoderCore.cs | 73 +++++++++++----- .../Formats/Icon/IconImageFormatDetector.cs | 6 +- 18 files changed, 263 insertions(+), 196 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 11c6aef29..c26536fd1 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -6,6 +6,7 @@ using System.Buffers.Binary; using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Common.Helpers; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -1599,6 +1600,14 @@ internal sealed class BmpDecoderCore : IImageDecoderInternals } } + if (palette.Length > 0) + { + Color[] colorTable = new Color[palette.Length / Unsafe.SizeOf()]; + ReadOnlySpan rgbTable = MemoryMarshal.Cast(palette); + Color.FromPixel(rgbTable, colorTable); + this.bmpMetadata.ColorTable = colorTable; + } + int skipAmount = 0; if (this.fileHeader.HasValue) { diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index f84e4f9c2..151da1828 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -109,6 +109,8 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals { this.memoryAllocator = memoryAllocator; this.bitsPerPixel = encoder.BitsPerPixel; + + // TODO: Use a palette quantizer if supplied. this.quantizer = encoder.Quantizer ?? KnownQuantizers.Octree; this.pixelSamplingStrategy = encoder.PixelSamplingStrategy; this.infoHeaderType = encoder.SupportTransparency ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion3; diff --git a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs index a2ed1d21d..a50023b27 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetadata.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Bmp; @@ -23,6 +23,11 @@ public class BmpMetadata : IDeepCloneable { this.BitsPerPixel = other.BitsPerPixel; this.InfoHeaderType = other.InfoHeaderType; + + if (other.ColorTable?.Length > 0) + { + this.ColorTable = other.ColorTable.Value.ToArray(); + } } /// @@ -35,8 +40,11 @@ public class BmpMetadata : IDeepCloneable /// public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24; + /// + /// Gets or sets the color table, if any. + /// + public ReadOnlyMemory? ColorTable { get; set; } + /// public IDeepCloneable DeepClone() => new BmpMetadata(this); - - // TODO: Colors used once we support encoding palette bmps. } diff --git a/src/ImageSharp/Formats/Cur/CurConstants.cs b/src/ImageSharp/Formats/Cur/CurConstants.cs index 6efd2817c..7abf4c812 100644 --- a/src/ImageSharp/Formats/Cur/CurConstants.cs +++ b/src/ImageSharp/Formats/Cur/CurConstants.cs @@ -9,20 +9,31 @@ namespace SixLabors.ImageSharp.Formats.Cur; internal static class CurConstants { /// - /// The list of mimetypes that equate to a ico. + /// The list of mime types that equate to a cur. /// /// /// See /// - public static readonly IEnumerable MimeTypes = new[] - { - "application/octet-stream", - }; + public static readonly IEnumerable MimeTypes = + [ + + // IANA-registered + "image/vnd.microsoft.icon", + + // ICO & CUR types used by Windows + "image/x-icon", + + // Erroneous types but have been used + "image/ico", + "image/icon", + "text/ico", + "application/ico", + ]; /// - /// The list of file extensions that equate to a ico. + /// The list of file extensions that equate to a cur. /// - public static readonly IEnumerable FileExtensions = new[] { "cur" }; + public static readonly IEnumerable FileExtensions = ["cur"]; public const uint FileHeader = 0x00_02_00_00; } diff --git a/src/ImageSharp/Formats/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs index 538f9a2c6..18ab8c75a 100644 --- a/src/ImageSharp/Formats/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs @@ -1,18 +1,24 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Icon; using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp.Formats.Cur; -internal sealed class CurDecoderCore(DecoderOptions options) : IconDecoderCore(options) +internal sealed class CurDecoderCore : IconDecoderCore { - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, Bmp.BmpBitsPerPixel bitsPerPixel) + public CurDecoderCore(DecoderOptions options) + : base(options) + { + } + + protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel) { CurFrameMetadata curFrameMetadata = metadata.GetCurMetadata(); curFrameMetadata.FromIconDirEntry(entry); curFrameMetadata.Compression = compression; - curFrameMetadata.BitsPerPixel = bitsPerPixel; + curFrameMetadata.BmpBitsPerPixel = bitsPerPixel; } } diff --git a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs index 3a7288b71..a6922d431 100644 --- a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs @@ -5,6 +5,10 @@ using SixLabors.ImageSharp.Formats.Icon; namespace SixLabors.ImageSharp.Formats.Cur; -internal sealed class CurEncoderCore() : IconEncoderCore(IconFileType.CUR) +internal sealed class CurEncoderCore : IconEncoderCore { + public CurEncoderCore() + : base(IconFileType.CUR) + { + } } diff --git a/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs b/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs index e8f3cfe8e..fc5cc5b2c 100644 --- a/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs +++ b/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Icon; namespace SixLabors.ImageSharp.Formats.Cur; @@ -17,70 +18,48 @@ public class CurFrameMetadata : IDeepCloneable, IDeepCloneable { } - /// - /// Initializes a new instance of the class. - /// - /// width - /// height - /// colorCount - /// hotspotX - /// hotspotY - public CurFrameMetadata(byte width, byte height, byte colorCount, ushort hotspotX, ushort hotspotY) - { - this.EncodingWidth = width; - this.EncodingHeight = height; - this.ColorCount = colorCount; - this.HotspotX = hotspotX; - this.HotspotY = hotspotY; - } - - /// - public CurFrameMetadata(CurFrameMetadata metadata) + private CurFrameMetadata(CurFrameMetadata metadata) { - this.EncodingWidth = metadata.EncodingWidth; - this.EncodingHeight = metadata.EncodingHeight; - this.ColorCount = metadata.ColorCount; + this.Compression = metadata.Compression; this.HotspotX = metadata.HotspotX; this.HotspotY = metadata.HotspotY; - this.Compression = metadata.Compression; + this.EncodingWidth = metadata.EncodingWidth; + this.EncodingHeight = metadata.EncodingHeight; + this.BmpBitsPerPixel = metadata.BmpBitsPerPixel; } /// - /// Gets or sets icoFrameCompression. + /// Gets or sets the frame compressions format. /// public IconFrameCompression Compression { get; set; } /// - /// Gets or sets ColorCount field.
- /// Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette. + /// Gets or sets the horizontal coordinates of the hotspot in number of pixels from the left. ///
- // TODO: BmpMetadata does not supported palette yet. - public byte ColorCount { get; set; } + public ushort HotspotX { get; set; } /// - /// Gets or sets Specifies the horizontal coordinates of the hotspot in number of pixels from the left. + /// Gets or sets the vertical coordinates of the hotspot in number of pixels from the top. /// - public ushort HotspotX { get; set; } + public ushort HotspotY { get; set; } /// - /// Gets or sets Specifies the vertical coordinates of the hotspot in number of pixels from the top. + /// Gets or sets the encoding width.
+ /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. ///
- public ushort HotspotY { get; set; } + public byte EncodingWidth { get; set; } /// - /// Gets or sets Height field.
- /// Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels. + /// Gets or sets the encoding height.
+ /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. ///
public byte EncodingHeight { get; set; } /// - /// Gets or sets Width field.
- /// Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels. + /// Gets or sets the number of bits per pixel.
+ /// Used when is ///
- public byte EncodingWidth { get; set; } - - /// - public Bmp.BmpBitsPerPixel BitsPerPixel { get; set; } = Bmp.BmpBitsPerPixel.Pixel24; + public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel32; /// public CurFrameMetadata DeepClone() => new(this); @@ -88,21 +67,27 @@ public class CurFrameMetadata : IDeepCloneable, IDeepCloneable /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); - internal void FromIconDirEntry(in IconDirEntry entry) + internal void FromIconDirEntry(IconDirEntry entry) { this.EncodingWidth = entry.Width; this.EncodingHeight = entry.Height; - this.ColorCount = entry.ColorCount; this.HotspotX = entry.Planes; this.HotspotY = entry.BitCount; } - internal IconDirEntry ToIconDirEntry() => new() + internal IconDirEntry ToIconDirEntry() { - Width = this.EncodingWidth, - Height = this.EncodingHeight, - ColorCount = this.ColorCount, - Planes = this.HotspotX, - BitCount = this.HotspotY, - }; + byte colorCount = this.Compression == IconFrameCompression.Png || this.BmpBitsPerPixel > BmpBitsPerPixel.Pixel8 + ? (byte)0 + : (byte)ColorNumerics.GetColorCountForBitDepth((int)this.BmpBitsPerPixel); + + return new() + { + Width = this.EncodingWidth, + Height = this.EncodingHeight, + Planes = this.HotspotX, + BitCount = this.HotspotY, + ColorCount = colorCount + }; + } } diff --git a/src/ImageSharp/Formats/Ico/IcoConstants.cs b/src/ImageSharp/Formats/Ico/IcoConstants.cs index 0b963a431..116579368 100644 --- a/src/ImageSharp/Formats/Ico/IcoConstants.cs +++ b/src/ImageSharp/Formats/Ico/IcoConstants.cs @@ -9,13 +9,14 @@ namespace SixLabors.ImageSharp.Formats.Ico; internal static class IcoConstants { /// - /// The list of mimetypes that equate to a ico. + /// The list of mime types that equate to a ico. /// /// /// See /// - public static readonly IEnumerable MimeTypes = new[] - { + public static readonly IEnumerable MimeTypes = + [ + // IANA-registered "image/vnd.microsoft.icon", @@ -27,12 +28,12 @@ internal static class IcoConstants "image/icon", "text/ico", "application/ico", - }; + ]; /// /// The list of file extensions that equate to a ico. /// - public static readonly IEnumerable FileExtensions = new[] { "ico" }; + public static readonly IEnumerable FileExtensions = ["ico"]; public const uint FileHeader = 0x00_01_00_00; } diff --git a/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs index 78cb0d961..e8629e35b 100644 --- a/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs @@ -1,18 +1,24 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Icon; using SixLabors.ImageSharp.Metadata; namespace SixLabors.ImageSharp.Formats.Ico; -internal sealed class IcoDecoderCore(DecoderOptions options) : IconDecoderCore(options) +internal sealed class IcoDecoderCore : IconDecoderCore { - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, Bmp.BmpBitsPerPixel bitsPerPixel) + public IcoDecoderCore(DecoderOptions options) + : base(options) + { + } + + protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel) { IcoFrameMetadata icoFrameMetadata = metadata.GetIcoMetadata(); icoFrameMetadata.FromIconDirEntry(entry); icoFrameMetadata.Compression = compression; - icoFrameMetadata.BitsPerPixel = bitsPerPixel; + icoFrameMetadata.BmpBitsPerPixel = bitsPerPixel; } } diff --git a/src/ImageSharp/Formats/Ico/IcoEncoder.cs b/src/ImageSharp/Formats/Ico/IcoEncoder.cs index 0668a7e23..298f93dec 100644 --- a/src/ImageSharp/Formats/Ico/IcoEncoder.cs +++ b/src/ImageSharp/Formats/Ico/IcoEncoder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors. +// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. namespace SixLabors.ImageSharp.Formats.Ico; @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Ico; /// /// Image encoder for writing an image to a stream as a Windows Icon. /// -public sealed class IcoEncoder : QuantizingImageEncoder +public sealed class IcoEncoder : ImageEncoder { /// protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) diff --git a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs index 12ced58fd..ab3edfbd3 100644 --- a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs @@ -5,6 +5,10 @@ using SixLabors.ImageSharp.Formats.Icon; namespace SixLabors.ImageSharp.Formats.Ico; -internal sealed class IcoEncoderCore() : IconEncoderCore(IconFileType.ICO) +internal sealed class IcoEncoderCore : IconEncoderCore { + public IcoEncoderCore() + : base(IconFileType.ICO) + { + } } diff --git a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs index 8d7eb17b5..82e4ce3b2 100644 --- a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs +++ b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs @@ -1,12 +1,13 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Icon; namespace SixLabors.ImageSharp.Formats.Ico; /// -/// IcoFrameMetadata. TODO: Remove base class and merge into this class. +/// Provides Ico specific metadata information for the image frame. /// public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable { @@ -17,54 +18,36 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable { } - /// - /// Initializes a new instance of the class. - /// - /// width - /// height - /// colorCount - public IcoFrameMetadata(byte width, byte height, byte colorCount) - { - this.EncodingWidth = width; - this.EncodingHeight = height; - this.ColorCount = colorCount; - } - - /// - public IcoFrameMetadata(IcoFrameMetadata metadata) + private IcoFrameMetadata(IcoFrameMetadata metadata) { + this.Compression = metadata.Compression; this.EncodingWidth = metadata.EncodingWidth; this.EncodingHeight = metadata.EncodingHeight; - this.ColorCount = metadata.ColorCount; - this.Compression = metadata.Compression; + this.BmpBitsPerPixel = metadata.BmpBitsPerPixel; } /// - /// Gets or sets icoFrameCompression. + /// Gets or sets the frame compressions format. /// public IconFrameCompression Compression { get; set; } /// - /// Gets or sets ColorCount field.
- /// Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette. + /// Gets or sets the encoding width.
+ /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. ///
- // TODO: BmpMetadata does not supported palette yet. - public byte ColorCount { get; set; } + public byte EncodingWidth { get; set; } /// - /// Gets or sets Height field.
- /// Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels. + /// Gets or sets the encoding height.
+ /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. ///
public byte EncodingHeight { get; set; } /// - /// Gets or sets Width field.
- /// Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels. + /// Gets or sets the number of bits per pixel.
+ /// Used when is ///
- public byte EncodingWidth { get; set; } - - /// - public Bmp.BmpBitsPerPixel BitsPerPixel { get; set; } = Bmp.BmpBitsPerPixel.Pixel24; + public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel32; /// public IcoFrameMetadata DeepClone() => new(this); @@ -72,24 +55,29 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable /// IDeepCloneable IDeepCloneable.DeepClone() => this.DeepClone(); - internal void FromIconDirEntry(in IconDirEntry entry) + internal void FromIconDirEntry(IconDirEntry entry) { this.EncodingWidth = entry.Width; this.EncodingHeight = entry.Height; - this.ColorCount = entry.ColorCount; } - internal IconDirEntry ToIconDirEntry() => new() + internal IconDirEntry ToIconDirEntry() { - Width = this.EncodingWidth, - Height = this.EncodingHeight, - ColorCount = this.ColorCount, - Planes = 1, - BitCount = this.Compression switch + byte colorCount = this.Compression == IconFrameCompression.Png || this.BmpBitsPerPixel > BmpBitsPerPixel.Pixel8 + ? (byte)0 + : (byte)ColorNumerics.GetColorCountForBitDepth((int)this.BmpBitsPerPixel); + + return new() { - IconFrameCompression.Bmp => (ushort)this.BitsPerPixel, - IconFrameCompression.Png => 32, - _ => throw new NotSupportedException($"Value: {this.Compression}"), - }, - }; + Width = this.EncodingWidth, + Height = this.EncodingHeight, + Planes = 1, + ColorCount = colorCount, + BitCount = this.Compression switch + { + IconFrameCompression.Bmp => (ushort)this.BmpBitsPerPixel, + IconFrameCompression.Png or _ => 32, + }, + }; + } } diff --git a/src/ImageSharp/Formats/Icon/IconAssert.cs b/src/ImageSharp/Formats/Icon/IconAssert.cs index 547b6a6eb..398a3e5f4 100644 --- a/src/ImageSharp/Formats/Icon/IconAssert.cs +++ b/src/ImageSharp/Formats/Icon/IconAssert.cs @@ -5,14 +5,6 @@ namespace SixLabors.ImageSharp.Formats.Icon; internal class IconAssert { - internal static void CanSeek(Stream stream) - { - if (!stream.CanSeek) - { - throw new NotSupportedException("This stream cannot support seek"); - } - } - internal static int EndOfStream(int v, int length) { if (v != length) @@ -22,28 +14,4 @@ internal class IconAssert return v; } - - internal static long EndOfStream(long v, long length) - { - if (v != length) - { - throw new EndOfStreamException(); - } - - return v; - } - - internal static byte Is1ByteSize(int i) - { - if (i is 256) - { - return 0; - } - else if (i > byte.MaxValue) - { - throw new FormatException("Image size Too Large."); - } - - return (byte)i; - } } diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index a502b91c9..97d0aec6d 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -10,12 +10,15 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Icon; -internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderInternals +internal abstract class IconDecoderCore : IImageDecoderInternals { private IconDir fileHeader; private IconDirEntry[]? entries; - public DecoderOptions Options { get; } = options; + protected IconDecoderCore(DecoderOptions options) + => this.Options = options; + + public DecoderOptions Options { get; } public Size Dimensions { get; private set; } @@ -61,10 +64,11 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI } ImageMetadata metadata = new(); + BmpMetadata? bmpMetadata = null; PngMetadata? pngMetadata = null; Image result = new(this.Options.Configuration, metadata, decodedEntries.Select(x => { - BmpBitsPerPixel bitsPerPixel = default; + BmpBitsPerPixel bitsPerPixel = BmpBitsPerPixel.Pixel32; ImageFrame target = new(this.Options.Configuration, this.Dimensions); ImageFrame source = x.Image.Frames.RootFrameUnsafe; for (int y = 0; y < source.Height; y++) @@ -80,12 +84,12 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI pngMetadata = x.Image.Metadata.GetPngMetadata(); } - // Bmp does not contain frame specific metadata. target.Metadata.SetFormatMetadata(PngFormat.Instance, target.Metadata.GetPngMetadata()); } else { - bitsPerPixel = x.Image.Metadata.GetBmpMetadata().BitsPerPixel; + bmpMetadata = x.Image.Metadata.GetBmpMetadata(); + bitsPerPixel = bmpMetadata.BitsPerPixel; } this.SetFrameMetadata(target.Metadata, this.entries[x.Index], x.Compression, bitsPerPixel); @@ -96,6 +100,11 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI }).ToArray()); // Copy the format specific metadata to the image. + if (bmpMetadata != null) + { + result.Metadata.SetFormatMetadata(BmpFormat.Instance, bmpMetadata); + } + if (pngMetadata != null) { result.Metadata.SetFormatMetadata(PngFormat.Instance, pngMetadata); @@ -114,9 +123,10 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI ImageMetadata metadata = new(); ImageFrameMetadata[] frames = new ImageFrameMetadata[this.fileHeader.Count]; + int bpp = 0; for (int i = 0; i < frames.Length; i++) { - BmpBitsPerPixel bitsPerPixel = default; + BmpBitsPerPixel bitsPerPixel = BmpBitsPerPixel.Pixel32; ref IconDirEntry entry = ref this.entries[i]; // If we hit the end of the stream we should break. @@ -140,11 +150,13 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI ImageInfo temp = this.GetDecoder(isPng).Identify(stream, cancellationToken); frames[i] = new(); - if (isPng) + if (!isPng) { bitsPerPixel = temp.Metadata.GetBmpMetadata().BitsPerPixel; } + bpp = Math.Max(bpp, (int)bitsPerPixel); + this.SetFrameMetadata(frames[i], this.entries[i], isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, bitsPerPixel); // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data @@ -152,7 +164,7 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI this.Dimensions = new(Math.Max(this.Dimensions.Width, temp.Size.Width), Math.Max(this.Dimensions.Height, temp.Size.Height)); } - return new(new(32), this.Dimensions, metadata, frames); + return new(new(bpp), this.Dimensions, metadata, frames); } protected abstract void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel); @@ -211,15 +223,13 @@ internal abstract class IconDecoderCore(DecoderOptions options) : IImageDecoderI GeneralOptions = this.Options, }); } - else + + return new BmpDecoderCore(new() { - return new BmpDecoderCore(new() - { - GeneralOptions = this.Options, - ProcessedAlphaMask = true, - SkipFileHeader = true, - UseDoubleHeight = true, - }); - } + GeneralOptions = this.Options, + ProcessedAlphaMask = true, + SkipFileHeader = true, + UseDoubleHeight = true, + }); } } diff --git a/src/ImageSharp/Formats/Icon/IconDir.cs b/src/ImageSharp/Formats/Icon/IconDir.cs index aa583ee1e..3e02538c8 100644 --- a/src/ImageSharp/Formats/Icon/IconDir.cs +++ b/src/ImageSharp/Formats/Icon/IconDir.cs @@ -9,8 +9,20 @@ namespace SixLabors.ImageSharp.Formats.Icon; internal struct IconDir(ushort reserved, IconFileType type, ushort count) { public const int Size = 3 * sizeof(ushort); + + /// + /// Reserved. Must always be 0. + /// public ushort Reserved = reserved; + + /// + /// Specifies image type: 1 for icon (.ICO) image, 2 for cursor (.CUR) image. Other values are invalid. + /// public IconFileType Type = type; + + /// + /// Specifies number of images in the file. + /// public ushort Count = count; public IconDir(IconFileType type) @@ -23,9 +35,9 @@ internal struct IconDir(ushort reserved, IconFileType type, ushort count) { } - public static IconDir Parse(in ReadOnlySpan data) + public static IconDir Parse(ReadOnlySpan data) => MemoryMarshal.Cast(data)[0]; - public unsafe void WriteTo(in Stream stream) + public readonly unsafe void WriteTo(Stream stream) => stream.Write(MemoryMarshal.Cast([this])); } diff --git a/src/ImageSharp/Formats/Icon/IconDirEntry.cs b/src/ImageSharp/Formats/Icon/IconDirEntry.cs index 7a8e09e37..eab15dd87 100644 --- a/src/ImageSharp/Formats/Icon/IconDirEntry.cs +++ b/src/ImageSharp/Formats/Icon/IconDirEntry.cs @@ -10,25 +10,51 @@ internal struct IconDirEntry { public const int Size = (4 * sizeof(byte)) + (2 * sizeof(ushort)) + (2 * sizeof(uint)); + /// + /// Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels. + /// public byte Width; + /// + /// Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels.[ + /// public byte Height; + /// + /// Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette. + /// public byte ColorCount; + /// + /// Reserved. Should be 0. + /// public byte Reserved; + /// + /// In ICO format: Specifies color planes. Should be 0 or 1.
+ /// In CUR format: Specifies the horizontal coordinates of the hotspot in number of pixels from the left. + ///
public ushort Planes; + /// + /// In ICO format: Specifies bits per pixel.
+ /// In CUR format: Specifies the vertical coordinates of the hotspot in number of pixels from the top. + ///
public ushort BitCount; + /// + /// Specifies the size of the image's data in bytes + /// public uint BytesInRes; + /// + /// Specifies the offset of BMP or PNG data from the beginning of the ICO/CUR file. + /// public uint ImageOffset; public static IconDirEntry Parse(in ReadOnlySpan data) => MemoryMarshal.Cast(data)[0]; - public unsafe void WriteTo(in Stream stream) + public readonly unsafe void WriteTo(in Stream stream) => stream.Write(MemoryMarshal.Cast([this])); } diff --git a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs index b4d563d4b..5332d9a86 100644 --- a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs @@ -2,18 +2,23 @@ // Licensed under the Six Labors Split License. using System.Diagnostics.CodeAnalysis; +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.Formats.Ico; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Icon; -internal abstract class IconEncoderCore(IconFileType iconFileType) - : IImageEncoderInternals +internal abstract class IconEncoderCore : IImageEncoderInternals { + private readonly IconFileType iconFileType; private IconDir fileHeader; + private EncodingFrameMetadata[]? entries; - private IconFrameMetadata[]? entries; + protected IconEncoderCore(IconFileType iconFileType) + => this.iconFileType = iconFileType; public void Encode( Image image, @@ -24,17 +29,18 @@ internal abstract class IconEncoderCore(IconFileType iconFileType) Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - IconAssert.CanSeek(stream); - // Stream may not at 0. long basePosition = stream.Position; this.InitHeader(image); + // We don't write the header and entries yet as we need to write the image data first. int dataOffset = IconDir.Size + (IconDirEntry.Size * this.entries.Length); _ = stream.Seek(dataOffset, SeekOrigin.Current); for (int i = 0; i < image.Frames.Count; i++) { + // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data + // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. ImageFrame frame = image.Frames[i]; int width = this.entries[i].Entry.Width; if (width is 0) @@ -50,36 +56,54 @@ internal abstract class IconEncoderCore(IconFileType iconFileType) this.entries[i].Entry.ImageOffset = (uint)stream.Position; - Image img = new(width, height); + // We crop the frame to the size specified in the metadata. + // TODO: we can optimize this by cropping the frame only if the new size is both required and different. + using Image encodingFrame = new(width, height); for (int y = 0; y < height; y++) { - frame.PixelBuffer.DangerousGetRowSpan(y)[..width].CopyTo(img.GetRootFramePixelBuffer().DangerousGetRowSpan(y)); + frame.PixelBuffer.DangerousGetRowSpan(y)[..width] + .CopyTo(encodingFrame.GetRootFramePixelBuffer().DangerousGetRowSpan(y)); } - QuantizingImageEncoder encoder = this.entries[i].Compression switch + ref EncodingFrameMetadata encodingMetadata = ref this.entries[i]; + + QuantizingImageEncoder encoder = encodingMetadata.Compression switch { - IconFrameCompression.Bmp => new Bmp.BmpEncoder() + IconFrameCompression.Bmp => new BmpEncoder() { + // We don't have access to the palette in the metadata so we need to quantize the image + // using a new one generated from the pixel data. + Quantizer = encodingMetadata.Entry.BitCount <= 8 + ? new WuQuantizer(new() + { + MaxColors = encodingMetadata.Entry.ColorCount + }) + : null, ProcessedAlphaMask = true, UseDoubleHeight = true, SkipFileHeader = true, SupportTransparency = false, - BitsPerPixel = iconFileType is IconFileType.ICO - ? (Bmp.BmpBitsPerPixel?)this.entries[i].Entry.BitCount - : Bmp.BmpBitsPerPixel.Pixel24 // TODO: Here you need to switch to selecting the corresponding value according to the size of the image + BitsPerPixel = encodingMetadata.BmpBitsPerPixel + }, + IconFrameCompression.Png => new PngEncoder() + { + // Only 32bit Png supported. + // https://devblogs.microsoft.com/oldnewthing/20101022-00/?p=12473 + BitDepth = PngBitDepth.Bit8, + ColorType = PngColorType.RgbWithAlpha }, - IconFrameCompression.Png => new Png.PngEncoder(), _ => throw new NotSupportedException(), }; - encoder.Encode(img, stream); - this.entries[i].Entry.BytesInRes = (uint)stream.Position - this.entries[i].Entry.ImageOffset; + encoder.Encode(encodingFrame, stream); + encodingMetadata.Entry.BytesInRes = (uint)stream.Position - encodingMetadata.Entry.ImageOffset; } + // We now need to rewind the stream and write the header and the entries. long endPosition = stream.Position; _ = stream.Seek(basePosition, SeekOrigin.Begin); this.fileHeader.WriteTo(stream); - foreach (IconFrameMetadata frame in this.entries) + foreach (EncodingFrameMetadata frame in this.entries) { frame.Entry.WriteTo(stream); } @@ -88,33 +112,38 @@ internal abstract class IconEncoderCore(IconFileType iconFileType) } [MemberNotNull(nameof(entries))] - private void InitHeader(in Image image) + private void InitHeader(Image image) { - this.fileHeader = new(iconFileType, (ushort)image.Frames.Count); - this.entries = iconFileType switch + this.fileHeader = new(this.iconFileType, (ushort)image.Frames.Count); + this.entries = this.iconFileType switch { IconFileType.ICO => image.Frames.Select(i => { IcoFrameMetadata metadata = i.Metadata.GetIcoMetadata(); - return new IconFrameMetadata(metadata.Compression, metadata.ToIconDirEntry()); + return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ToIconDirEntry()); }).ToArray(), IconFileType.CUR => image.Frames.Select(i => { CurFrameMetadata metadata = i.Metadata.GetCurMetadata(); - return new IconFrameMetadata(metadata.Compression, metadata.ToIconDirEntry()); + return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ToIconDirEntry()); }).ToArray(), _ => throw new NotSupportedException(), }; } - internal sealed class IconFrameMetadata(IconFrameCompression compression, IconDirEntry iconDirEntry) + internal sealed class EncodingFrameMetadata( + IconFrameCompression compression, + BmpBitsPerPixel bmpBitsPerPixel, + IconDirEntry iconDirEntry) { private IconDirEntry iconDirEntry = iconDirEntry; public IconFrameCompression Compression { get; set; } = compression; + public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = bmpBitsPerPixel; + public ref IconDirEntry Entry => ref this.iconDirEntry; } } diff --git a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs index b66b6c79f..9e7d22de2 100644 --- a/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Icon/IconImageFormatDetector.cs @@ -60,9 +60,7 @@ public class IconImageFormatDetector : IImageFormatDetector return true; } - else - { - return false; - } + + return false; } } From 7c0cd0baab036af0d1726976631beb9beef1487d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 18 Jun 2024 21:55:51 +1000 Subject: [PATCH 68/79] Merge separate assert file --- src/ImageSharp/Formats/Icon/IconAssert.cs | 17 ----------------- src/ImageSharp/Formats/Icon/IconDecoderCore.cs | 14 ++++++++++++-- src/ImageSharp/Formats/Icon/IconEncoderCore.cs | 3 ++- 3 files changed, 14 insertions(+), 20 deletions(-) delete mode 100644 src/ImageSharp/Formats/Icon/IconAssert.cs diff --git a/src/ImageSharp/Formats/Icon/IconAssert.cs b/src/ImageSharp/Formats/Icon/IconAssert.cs deleted file mode 100644 index 398a3e5f4..000000000 --- a/src/ImageSharp/Formats/Icon/IconAssert.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Icon; - -internal class IconAssert -{ - internal static int EndOfStream(int v, int length) - { - if (v != length) - { - throw new EndOfStreamException(); - } - - return v; - } -} diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index 97d0aec6d..a0849fa61 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -175,14 +175,14 @@ internal abstract class IconDecoderCore : IImageDecoderInternals Span buffer = stackalloc byte[IconDirEntry.Size]; // ICONDIR - _ = IconAssert.EndOfStream(stream.Read(buffer[..IconDir.Size]), IconDir.Size); + _ = CheckEndOfStream(stream.Read(buffer[..IconDir.Size]), IconDir.Size); this.fileHeader = IconDir.Parse(buffer); // ICONDIRENTRY this.entries = new IconDirEntry[this.fileHeader.Count]; for (int i = 0; i < this.entries.Length; i++) { - _ = IconAssert.EndOfStream(stream.Read(buffer[..IconDirEntry.Size]), IconDirEntry.Size); + _ = CheckEndOfStream(stream.Read(buffer[..IconDirEntry.Size]), IconDirEntry.Size); this.entries[i] = IconDirEntry.Parse(buffer); } @@ -232,4 +232,14 @@ internal abstract class IconDecoderCore : IImageDecoderInternals UseDoubleHeight = true, }); } + + private static int CheckEndOfStream(int v, int length) + { + if (v != length) + { + throw new InvalidImageContentException("Not enough bytes to read icon header."); + } + + return v; + } } diff --git a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs index 5332d9a86..eb07ab483 100644 --- a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs @@ -90,7 +90,8 @@ internal abstract class IconEncoderCore : IImageEncoderInternals // Only 32bit Png supported. // https://devblogs.microsoft.com/oldnewthing/20101022-00/?p=12473 BitDepth = PngBitDepth.Bit8, - ColorType = PngColorType.RgbWithAlpha + ColorType = PngColorType.RgbWithAlpha, + CompressionLevel = PngCompressionLevel.BestCompression }, _ => throw new NotSupportedException(), }; From b222a6733e2a978a950b783a7a96979271001841 Mon Sep 17 00:00:00 2001 From: Poker Date: Tue, 18 Jun 2024 21:07:08 +0800 Subject: [PATCH 69/79] Add test files --- .../Formats/Icon/Cur/CurEncoderTests.cs | 9 ++ .../Formats/Icon/Ico/IcoDecoderTests.cs | 119 ++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 114 +++++++++++++++++ tests/Images/Input/Icon/1bpp_size_15x15.ico | 3 + tests/Images/Input/Icon/1bpp_size_16x16.ico | 3 + tests/Images/Input/Icon/1bpp_size_17x17.ico | 3 + tests/Images/Input/Icon/1bpp_size_1x1.ico | 3 + tests/Images/Input/Icon/1bpp_size_256x256.ico | 3 + tests/Images/Input/Icon/1bpp_size_2x2.ico | 3 + tests/Images/Input/Icon/1bpp_size_31x31.ico | 3 + tests/Images/Input/Icon/1bpp_size_32x32.ico | 3 + tests/Images/Input/Icon/1bpp_size_33x33.ico | 3 + tests/Images/Input/Icon/1bpp_size_3x3.ico | 3 + tests/Images/Input/Icon/1bpp_size_4x4.ico | 3 + tests/Images/Input/Icon/1bpp_size_5x5.ico | 3 + tests/Images/Input/Icon/1bpp_size_6x6.ico | 3 + tests/Images/Input/Icon/1bpp_size_7x7.ico | 3 + tests/Images/Input/Icon/1bpp_size_8x8.ico | 3 + tests/Images/Input/Icon/1bpp_size_9x9.ico | 3 + .../Input/Icon/1bpp_transp_not_square.ico | 3 + .../Images/Input/Icon/1bpp_transp_partial.ico | 3 + tests/Images/Input/Icon/24bpp_size_15x15.ico | 3 + tests/Images/Input/Icon/24bpp_size_16x16.ico | 3 + tests/Images/Input/Icon/24bpp_size_17x17.ico | 3 + tests/Images/Input/Icon/24bpp_size_1x1.ico | 3 + .../Images/Input/Icon/24bpp_size_256x256.ico | 3 + tests/Images/Input/Icon/24bpp_size_2x2.ico | 3 + tests/Images/Input/Icon/24bpp_size_31x31.ico | 3 + tests/Images/Input/Icon/24bpp_size_32x32.ico | 3 + tests/Images/Input/Icon/24bpp_size_33x33.ico | 3 + tests/Images/Input/Icon/24bpp_size_3x3.ico | 3 + tests/Images/Input/Icon/24bpp_size_4x4.ico | 3 + tests/Images/Input/Icon/24bpp_size_5x5.ico | 3 + tests/Images/Input/Icon/24bpp_size_6x6.ico | 3 + tests/Images/Input/Icon/24bpp_size_7x7.ico | 3 + tests/Images/Input/Icon/24bpp_size_8x8.ico | 3 + tests/Images/Input/Icon/24bpp_size_9x9.ico | 3 + tests/Images/Input/Icon/24bpp_transp.ico | 3 + .../Input/Icon/24bpp_transp_not_square.ico | 3 + .../Input/Icon/24bpp_transp_partial.ico | 3 + tests/Images/Input/Icon/32bpp_size_15x15.ico | 3 + tests/Images/Input/Icon/32bpp_size_16x16.ico | 3 + tests/Images/Input/Icon/32bpp_size_17x17.ico | 3 + tests/Images/Input/Icon/32bpp_size_1x1.ico | 3 + .../Images/Input/Icon/32bpp_size_256x256.ico | 3 + tests/Images/Input/Icon/32bpp_size_2x2.ico | 3 + tests/Images/Input/Icon/32bpp_size_31x31.ico | 3 + tests/Images/Input/Icon/32bpp_size_32x32.ico | 3 + tests/Images/Input/Icon/32bpp_size_33x33.ico | 3 + tests/Images/Input/Icon/32bpp_size_3x3.ico | 3 + tests/Images/Input/Icon/32bpp_size_4x4.ico | 3 + tests/Images/Input/Icon/32bpp_size_5x5.ico | 3 + tests/Images/Input/Icon/32bpp_size_6x6.ico | 3 + tests/Images/Input/Icon/32bpp_size_7x7.ico | 3 + tests/Images/Input/Icon/32bpp_size_8x8.ico | 3 + tests/Images/Input/Icon/32bpp_size_9x9.ico | 3 + tests/Images/Input/Icon/32bpp_transp.ico | 3 + .../Input/Icon/32bpp_transp_not_square.ico | 3 + .../Input/Icon/32bpp_transp_partial.ico | 3 + tests/Images/Input/Icon/4bpp_size_15x15.ico | 3 + tests/Images/Input/Icon/4bpp_size_16x16.ico | 3 + tests/Images/Input/Icon/4bpp_size_17x17.ico | 3 + tests/Images/Input/Icon/4bpp_size_1x1.ico | 3 + tests/Images/Input/Icon/4bpp_size_256x256.ico | 3 + tests/Images/Input/Icon/4bpp_size_2x2.ico | 3 + tests/Images/Input/Icon/4bpp_size_31x31.ico | 3 + tests/Images/Input/Icon/4bpp_size_32x32.ico | 3 + tests/Images/Input/Icon/4bpp_size_33x33.ico | 3 + tests/Images/Input/Icon/4bpp_size_3x3.ico | 3 + tests/Images/Input/Icon/4bpp_size_4x4.ico | 3 + tests/Images/Input/Icon/4bpp_size_5x5.ico | 3 + tests/Images/Input/Icon/4bpp_size_6x6.ico | 3 + tests/Images/Input/Icon/4bpp_size_7x7.ico | 3 + tests/Images/Input/Icon/4bpp_size_8x8.ico | 3 + tests/Images/Input/Icon/4bpp_size_9x9.ico | 3 + .../Input/Icon/4bpp_transp_not_square.ico | 3 + .../Images/Input/Icon/4bpp_transp_partial.ico | 3 + tests/Images/Input/Icon/8bpp_size_15x15.ico | 3 + tests/Images/Input/Icon/8bpp_size_16x16.ico | 3 + tests/Images/Input/Icon/8bpp_size_17x17.ico | 3 + tests/Images/Input/Icon/8bpp_size_1x1.ico | 3 + tests/Images/Input/Icon/8bpp_size_256x256.ico | 3 + tests/Images/Input/Icon/8bpp_size_2x2.ico | 3 + tests/Images/Input/Icon/8bpp_size_31x31.ico | 3 + tests/Images/Input/Icon/8bpp_size_32x32.ico | 3 + tests/Images/Input/Icon/8bpp_size_33x33.ico | 3 + tests/Images/Input/Icon/8bpp_size_3x3.ico | 3 + tests/Images/Input/Icon/8bpp_size_4x4.ico | 3 + tests/Images/Input/Icon/8bpp_size_5x5.ico | 3 + tests/Images/Input/Icon/8bpp_size_6x6.ico | 3 + tests/Images/Input/Icon/8bpp_size_7x7.ico | 3 + tests/Images/Input/Icon/8bpp_size_8x8.ico | 3 + tests/Images/Input/Icon/8bpp_size_9x9.ico | 3 + .../Input/Icon/8bpp_transp_not_square.ico | 3 + .../Images/Input/Icon/8bpp_transp_partial.ico | 3 + tests/Images/Input/Icon/cur_fake.ico | 3 + tests/Images/Input/Icon/cur_real.cur | 3 + tests/Images/Input/Icon/ico_fake.cur | 3 + tests/Images/Input/Icon/invalid_RLE4.ico | 3 + tests/Images/Input/Icon/invalid_RLE8.ico | 3 + tests/Images/Input/Icon/invalid_all.ico | 3 + tests/Images/Input/Icon/invalid_bpp.ico | 3 + .../Images/Input/Icon/invalid_compression.ico | 3 + tests/Images/Input/Icon/invalid_png.ico | 3 + tests/Images/Input/Icon/mixed_bmp_png_a.ico | 3 + tests/Images/Input/Icon/mixed_bmp_png_b.ico | 3 + tests/Images/Input/Icon/mixed_bmp_png_c.ico | 3 + tests/Images/Input/Icon/multi_size_a.ico | 3 + tests/Images/Input/Icon/multi_size_b.ico | 3 + tests/Images/Input/Icon/multi_size_c.ico | 3 + tests/Images/Input/Icon/multi_size_d.ico | 3 + tests/Images/Input/Icon/multi_size_e.ico | 3 + tests/Images/Input/Icon/multi_size_f.ico | 3 + .../Input/Icon/multi_size_multi_bits_a.ico | 3 + .../Input/Icon/multi_size_multi_bits_b.ico | 3 + .../Input/Icon/multi_size_multi_bits_c.ico | 3 + .../Input/Icon/multi_size_multi_bits_d.ico | 3 + 117 files changed, 584 insertions(+) create mode 100644 tests/Images/Input/Icon/1bpp_size_15x15.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_16x16.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_17x17.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_1x1.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_256x256.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_2x2.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_31x31.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_32x32.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_33x33.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_3x3.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_4x4.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_5x5.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_6x6.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_7x7.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_8x8.ico create mode 100644 tests/Images/Input/Icon/1bpp_size_9x9.ico create mode 100644 tests/Images/Input/Icon/1bpp_transp_not_square.ico create mode 100644 tests/Images/Input/Icon/1bpp_transp_partial.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_15x15.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_16x16.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_17x17.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_1x1.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_256x256.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_2x2.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_31x31.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_32x32.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_33x33.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_3x3.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_4x4.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_5x5.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_6x6.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_7x7.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_8x8.ico create mode 100644 tests/Images/Input/Icon/24bpp_size_9x9.ico create mode 100644 tests/Images/Input/Icon/24bpp_transp.ico create mode 100644 tests/Images/Input/Icon/24bpp_transp_not_square.ico create mode 100644 tests/Images/Input/Icon/24bpp_transp_partial.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_15x15.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_16x16.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_17x17.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_1x1.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_256x256.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_2x2.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_31x31.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_32x32.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_33x33.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_3x3.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_4x4.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_5x5.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_6x6.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_7x7.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_8x8.ico create mode 100644 tests/Images/Input/Icon/32bpp_size_9x9.ico create mode 100644 tests/Images/Input/Icon/32bpp_transp.ico create mode 100644 tests/Images/Input/Icon/32bpp_transp_not_square.ico create mode 100644 tests/Images/Input/Icon/32bpp_transp_partial.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_15x15.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_16x16.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_17x17.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_1x1.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_256x256.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_2x2.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_31x31.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_32x32.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_33x33.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_3x3.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_4x4.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_5x5.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_6x6.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_7x7.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_8x8.ico create mode 100644 tests/Images/Input/Icon/4bpp_size_9x9.ico create mode 100644 tests/Images/Input/Icon/4bpp_transp_not_square.ico create mode 100644 tests/Images/Input/Icon/4bpp_transp_partial.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_15x15.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_16x16.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_17x17.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_1x1.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_256x256.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_2x2.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_31x31.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_32x32.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_33x33.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_3x3.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_4x4.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_5x5.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_6x6.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_7x7.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_8x8.ico create mode 100644 tests/Images/Input/Icon/8bpp_size_9x9.ico create mode 100644 tests/Images/Input/Icon/8bpp_transp_not_square.ico create mode 100644 tests/Images/Input/Icon/8bpp_transp_partial.ico create mode 100644 tests/Images/Input/Icon/cur_fake.ico create mode 100644 tests/Images/Input/Icon/cur_real.cur create mode 100644 tests/Images/Input/Icon/ico_fake.cur create mode 100644 tests/Images/Input/Icon/invalid_RLE4.ico create mode 100644 tests/Images/Input/Icon/invalid_RLE8.ico create mode 100644 tests/Images/Input/Icon/invalid_all.ico create mode 100644 tests/Images/Input/Icon/invalid_bpp.ico create mode 100644 tests/Images/Input/Icon/invalid_compression.ico create mode 100644 tests/Images/Input/Icon/invalid_png.ico create mode 100644 tests/Images/Input/Icon/mixed_bmp_png_a.ico create mode 100644 tests/Images/Input/Icon/mixed_bmp_png_b.ico create mode 100644 tests/Images/Input/Icon/mixed_bmp_png_c.ico create mode 100644 tests/Images/Input/Icon/multi_size_a.ico create mode 100644 tests/Images/Input/Icon/multi_size_b.ico create mode 100644 tests/Images/Input/Icon/multi_size_c.ico create mode 100644 tests/Images/Input/Icon/multi_size_d.ico create mode 100644 tests/Images/Input/Icon/multi_size_e.ico create mode 100644 tests/Images/Input/Icon/multi_size_f.ico create mode 100644 tests/Images/Input/Icon/multi_size_multi_bits_a.ico create mode 100644 tests/Images/Input/Icon/multi_size_multi_bits_b.ico create mode 100644 tests/Images/Input/Icon/multi_size_multi_bits_c.ico create mode 100644 tests/Images/Input/Icon/multi_size_multi_bits_d.ico diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs index 9908786f1..58213397c 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics; using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Cur; @@ -29,4 +30,12 @@ public class CurEncoderTests memStream.Seek(0, SeekOrigin.Begin); CurDecoder.Instance.Decode(new(), memStream); } + + [Theory] + [WithFile(CurFake, PixelTypes.Rgba32)] + [WithFile(CurReal, PixelTypes.Rgba32)] + public void CurDecoder_Decode2(TestImageProvider provider) + { + Debug.Assert(false); + } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index 56378653a..ab4ec0a3d 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Diagnostics; using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Ico; @@ -21,4 +22,122 @@ public class IcoDecoderTests // TODO: Assert metadata, frame count, etc } + + [Theory] + [WithFile(Bpp1_size_15x15, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_16x16, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_17x17, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_1x1, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_256x256, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_2x2, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_31x31, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_32x32, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_33x33, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_3x3, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_4x4, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_5x5, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_6x6, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_7x7, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_8x8, PixelTypes.Rgba32)] + [WithFile(Bpp1_size_9x9, PixelTypes.Rgba32)] + [WithFile(Bpp1_transp_not_square, PixelTypes.Rgba32)] + [WithFile(Bpp1_transp_partial, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_15x15, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_16x16, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_17x17, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_1x1, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_256x256, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_2x2, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_31x31, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_32x32, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_33x33, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_3x3, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_4x4, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_5x5, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_6x6, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_7x7, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_8x8, PixelTypes.Rgba32)] + [WithFile(Bpp24_size_9x9, PixelTypes.Rgba32)] + [WithFile(Bpp24_transp_not_square, PixelTypes.Rgba32)] + [WithFile(Bpp24_transp_partial, PixelTypes.Rgba32)] + [WithFile(Bpp24_transp, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_15x15, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_16x16, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_17x17, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_1x1, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_256x256, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_2x2, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_31x31, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_32x32, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_33x33, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_3x3, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_4x4, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_5x5, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_6x6, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_7x7, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_8x8, PixelTypes.Rgba32)] + [WithFile(Bpp32_size_9x9, PixelTypes.Rgba32)] + [WithFile(Bpp32_transp_not_square, PixelTypes.Rgba32)] + [WithFile(Bpp32_transp_partial, PixelTypes.Rgba32)] + [WithFile(Bpp32_transp, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_15x15, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_16x16, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_17x17, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_1x1, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_256x256, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_2x2, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_31x31, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_32x32, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_33x33, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_3x3, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_4x4, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_5x5, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_6x6, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_7x7, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_8x8, PixelTypes.Rgba32)] + [WithFile(Bpp4_size_9x9, PixelTypes.Rgba32)] + [WithFile(Bpp4_transp_not_square, PixelTypes.Rgba32)] + [WithFile(Bpp4_transp_partial, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_15x15, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_16x16, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_17x17, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_1x1, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_256x256, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_2x2, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_31x31, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_32x32, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_33x33, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_3x3, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_4x4, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_5x5, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_6x6, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_7x7, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_8x8, PixelTypes.Rgba32)] + [WithFile(Bpp8_size_9x9, PixelTypes.Rgba32)] + [WithFile(Bpp8_transp_not_square, PixelTypes.Rgba32)] + [WithFile(Bpp8_transp_partial, PixelTypes.Rgba32)] + [WithFile(IcoFake, PixelTypes.Rgba32)] + [WithFile(Invalid_all, PixelTypes.Rgba32)] + [WithFile(Invalid_bpp, PixelTypes.Rgba32)] + [WithFile(Invalid_compression, PixelTypes.Rgba32)] + [WithFile(Invalid_png, PixelTypes.Rgba32)] + [WithFile(Invalid_RLE4, PixelTypes.Rgba32)] + [WithFile(Invalid_RLE8, PixelTypes.Rgba32)] + [WithFile(Mixed_bmp_png_a, PixelTypes.Rgba32)] + [WithFile(Mixed_bmp_png_b, PixelTypes.Rgba32)] + [WithFile(Mixed_bmp_png_c, PixelTypes.Rgba32)] + [WithFile(Multi_size_a, PixelTypes.Rgba32)] + [WithFile(Multi_size_b, PixelTypes.Rgba32)] + [WithFile(Multi_size_c, PixelTypes.Rgba32)] + [WithFile(Multi_size_d, PixelTypes.Rgba32)] + [WithFile(Multi_size_e, PixelTypes.Rgba32)] + [WithFile(Multi_size_f, PixelTypes.Rgba32)] + [WithFile(Multi_size_multi_bits_a, PixelTypes.Rgba32)] + [WithFile(Multi_size_multi_bits_b, PixelTypes.Rgba32)] + [WithFile(Multi_size_multi_bits_c, PixelTypes.Rgba32)] + [WithFile(Multi_size_multi_bits_d, PixelTypes.Rgba32)] + public void IcoDecoder_Decode2(TestImageProvider provider) + { + Debug.Assert(false); + } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 8b76abca2..6db11c78f 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1126,10 +1126,124 @@ public static class TestImages public static class Ico { public const string Flutter = "Icon/flutter.ico"; + public const string Bpp1_size_15x15 = "Icon/1bpp_size_15x15.ico"; + public const string Bpp1_size_16x16 = "Icon/1bpp_size_16x16.ico"; + public const string Bpp1_size_17x17 = "Icon/1bpp_size_17x17.ico"; + public const string Bpp1_size_1x1 = "Icon/1bpp_size_1x1.ico"; + public const string Bpp1_size_256x256 = "Icon/1bpp_size_256x256.ico"; + public const string Bpp1_size_2x2 = "Icon/1bpp_size_2x2.ico"; + public const string Bpp1_size_31x31 = "Icon/1bpp_size_31x31.ico"; + public const string Bpp1_size_32x32 = "Icon/1bpp_size_32x32.ico"; + public const string Bpp1_size_33x33 = "Icon/1bpp_size_33x33.ico"; + public const string Bpp1_size_3x3 = "Icon/1bpp_size_3x3.ico"; + public const string Bpp1_size_4x4 = "Icon/1bpp_size_4x4.ico"; + public const string Bpp1_size_5x5 = "Icon/1bpp_size_5x5.ico"; + public const string Bpp1_size_6x6 = "Icon/1bpp_size_6x6.ico"; + public const string Bpp1_size_7x7 = "Icon/1bpp_size_7x7.ico"; + public const string Bpp1_size_8x8 = "Icon/1bpp_size_8x8.ico"; + public const string Bpp1_size_9x9 = "Icon/1bpp_size_9x9.ico"; + public const string Bpp1_transp_not_square = "Icon/1bpp_transp_not_square.ico"; + public const string Bpp1_transp_partial = "Icon/1bpp_transp_partial.ico"; + public const string Bpp24_size_15x15 = "Icon/24bpp_size_15x15.ico"; + public const string Bpp24_size_16x16 = "Icon/24bpp_size_16x16.ico"; + public const string Bpp24_size_17x17 = "Icon/24bpp_size_17x17.ico"; + public const string Bpp24_size_1x1 = "Icon/24bpp_size_1x1.ico"; + public const string Bpp24_size_256x256 = "Icon/24bpp_size_256x256.ico"; + public const string Bpp24_size_2x2 = "Icon/24bpp_size_2x2.ico"; + public const string Bpp24_size_31x31 = "Icon/24bpp_size_31x31.ico"; + public const string Bpp24_size_32x32 = "Icon/24bpp_size_32x32.ico"; + public const string Bpp24_size_33x33 = "Icon/24bpp_size_33x33.ico"; + public const string Bpp24_size_3x3 = "Icon/24bpp_size_3x3.ico"; + public const string Bpp24_size_4x4 = "Icon/24bpp_size_4x4.ico"; + public const string Bpp24_size_5x5 = "Icon/24bpp_size_5x5.ico"; + public const string Bpp24_size_6x6 = "Icon/24bpp_size_6x6.ico"; + public const string Bpp24_size_7x7 = "Icon/24bpp_size_7x7.ico"; + public const string Bpp24_size_8x8 = "Icon/24bpp_size_8x8.ico"; + public const string Bpp24_size_9x9 = "Icon/24bpp_size_9x9.ico"; + public const string Bpp24_transp_not_square = "Icon/24bpp_transp_not_square.ico"; + public const string Bpp24_transp_partial = "Icon/24bpp_transp_partial.ico"; + public const string Bpp24_transp = "Icon/24bpp_transp.ico"; + public const string Bpp32_size_15x15 = "Icon/32bpp_size_15x15.ico"; + public const string Bpp32_size_16x16 = "Icon/32bpp_size_16x16.ico"; + public const string Bpp32_size_17x17 = "Icon/32bpp_size_17x17.ico"; + public const string Bpp32_size_1x1 = "Icon/32bpp_size_1x1.ico"; + public const string Bpp32_size_256x256 = "Icon/32bpp_size_256x256.ico"; + public const string Bpp32_size_2x2 = "Icon/32bpp_size_2x2.ico"; + public const string Bpp32_size_31x31 = "Icon/32bpp_size_31x31.ico"; + public const string Bpp32_size_32x32 = "Icon/32bpp_size_32x32.ico"; + public const string Bpp32_size_33x33 = "Icon/32bpp_size_33x33.ico"; + public const string Bpp32_size_3x3 = "Icon/32bpp_size_3x3.ico"; + public const string Bpp32_size_4x4 = "Icon/32bpp_size_4x4.ico"; + public const string Bpp32_size_5x5 = "Icon/32bpp_size_5x5.ico"; + public const string Bpp32_size_6x6 = "Icon/32bpp_size_6x6.ico"; + public const string Bpp32_size_7x7 = "Icon/32bpp_size_7x7.ico"; + public const string Bpp32_size_8x8 = "Icon/32bpp_size_8x8.ico"; + public const string Bpp32_size_9x9 = "Icon/32bpp_size_9x9.ico"; + public const string Bpp32_transp_not_square = "Icon/32bpp_transp_not_square.ico"; + public const string Bpp32_transp_partial = "Icon/32bpp_transp_partial.ico"; + public const string Bpp32_transp = "Icon/32bpp_transp.ico"; + public const string Bpp4_size_15x15 = "Icon/4bpp_size_15x15.ico"; + public const string Bpp4_size_16x16 = "Icon/4bpp_size_16x16.ico"; + public const string Bpp4_size_17x17 = "Icon/4bpp_size_17x17.ico"; + public const string Bpp4_size_1x1 = "Icon/4bpp_size_1x1.ico"; + public const string Bpp4_size_256x256 = "Icon/4bpp_size_256x256.ico"; + public const string Bpp4_size_2x2 = "Icon/4bpp_size_2x2.ico"; + public const string Bpp4_size_31x31 = "Icon/4bpp_size_31x31.ico"; + public const string Bpp4_size_32x32 = "Icon/4bpp_size_32x32.ico"; + public const string Bpp4_size_33x33 = "Icon/4bpp_size_33x33.ico"; + public const string Bpp4_size_3x3 = "Icon/4bpp_size_3x3.ico"; + public const string Bpp4_size_4x4 = "Icon/4bpp_size_4x4.ico"; + public const string Bpp4_size_5x5 = "Icon/4bpp_size_5x5.ico"; + public const string Bpp4_size_6x6 = "Icon/4bpp_size_6x6.ico"; + public const string Bpp4_size_7x7 = "Icon/4bpp_size_7x7.ico"; + public const string Bpp4_size_8x8 = "Icon/4bpp_size_8x8.ico"; + public const string Bpp4_size_9x9 = "Icon/4bpp_size_9x9.ico"; + public const string Bpp4_transp_not_square = "Icon/4bpp_transp_not_square.ico"; + public const string Bpp4_transp_partial = "Icon/4bpp_transp_partial.ico"; + public const string Bpp8_size_15x15 = "Icon/8bpp_size_15x15.ico"; + public const string Bpp8_size_16x16 = "Icon/8bpp_size_16x16.ico"; + public const string Bpp8_size_17x17 = "Icon/8bpp_size_17x17.ico"; + public const string Bpp8_size_1x1 = "Icon/8bpp_size_1x1.ico"; + public const string Bpp8_size_256x256 = "Icon/8bpp_size_256x256.ico"; + public const string Bpp8_size_2x2 = "Icon/8bpp_size_2x2.ico"; + public const string Bpp8_size_31x31 = "Icon/8bpp_size_31x31.ico"; + public const string Bpp8_size_32x32 = "Icon/8bpp_size_32x32.ico"; + public const string Bpp8_size_33x33 = "Icon/8bpp_size_33x33.ico"; + public const string Bpp8_size_3x3 = "Icon/8bpp_size_3x3.ico"; + public const string Bpp8_size_4x4 = "Icon/8bpp_size_4x4.ico"; + public const string Bpp8_size_5x5 = "Icon/8bpp_size_5x5.ico"; + public const string Bpp8_size_6x6 = "Icon/8bpp_size_6x6.ico"; + public const string Bpp8_size_7x7 = "Icon/8bpp_size_7x7.ico"; + public const string Bpp8_size_8x8 = "Icon/8bpp_size_8x8.ico"; + public const string Bpp8_size_9x9 = "Icon/8bpp_size_9x9.ico"; + public const string Bpp8_transp_not_square = "Icon/8bpp_transp_not_square.ico"; + public const string Bpp8_transp_partial = "Icon/8bpp_transp_partial.ico"; + public const string Invalid_all = "Icon/invalid_all.ico"; + public const string IcoFake = "Icon/ico_fake.cur"; + public const string Invalid_bpp = "Icon/invalid_bpp.ico"; + public const string Invalid_compression = "Icon/invalid_compression.ico"; + public const string Invalid_png = "Icon/invalid_png.ico"; + public const string Invalid_RLE4 = "Icon/invalid_RLE4.ico"; + public const string Invalid_RLE8 = "Icon/invalid_RLE8.ico"; + public const string Mixed_bmp_png_a = "Icon/mixed_bmp_png_a.ico"; + public const string Mixed_bmp_png_b = "Icon/mixed_bmp_png_b.ico"; + public const string Mixed_bmp_png_c = "Icon/mixed_bmp_png_c.ico"; + public const string Multi_size_a = "Icon/multi_size_a.ico"; + public const string Multi_size_b = "Icon/multi_size_b.ico"; + public const string Multi_size_c = "Icon/multi_size_c.ico"; + public const string Multi_size_d = "Icon/multi_size_d.ico"; + public const string Multi_size_e = "Icon/multi_size_e.ico"; + public const string Multi_size_f = "Icon/multi_size_f.ico"; + public const string Multi_size_multi_bits_a = "Icon/multi_size_multi_bits_a.ico"; + public const string Multi_size_multi_bits_b = "Icon/multi_size_multi_bits_b.ico"; + public const string Multi_size_multi_bits_c = "Icon/multi_size_multi_bits_c.ico"; + public const string Multi_size_multi_bits_d = "Icon/multi_size_multi_bits_d.ico"; } public static class Cur { public const string WindowsMouse = "Icon/aero_arrow.cur"; + public const string CurReal = "Icon/cur_real.cur"; + public const string CurFake = "Icon/cur_fake.ico"; } } diff --git a/tests/Images/Input/Icon/1bpp_size_15x15.ico b/tests/Images/Input/Icon/1bpp_size_15x15.ico new file mode 100644 index 000000000..39fc9c521 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_15x15.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:846dda605ee23bb641534b272fa57300eacd85038feea5dd1a3f6d4b543a935e +size 190 diff --git a/tests/Images/Input/Icon/1bpp_size_16x16.ico b/tests/Images/Input/Icon/1bpp_size_16x16.ico new file mode 100644 index 000000000..6179678bc --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_16x16.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:029d0438eda83d4d9e087cf79abe2d0234728c37f570de808355c0e79c71be17 +size 198 diff --git a/tests/Images/Input/Icon/1bpp_size_17x17.ico b/tests/Images/Input/Icon/1bpp_size_17x17.ico new file mode 100644 index 000000000..90138a08d --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_17x17.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8db024a49fdd91c9d5d1fc0c1d6f60991518a5776031f58cdafbdd3ed9e4f26b +size 206 diff --git a/tests/Images/Input/Icon/1bpp_size_1x1.ico b/tests/Images/Input/Icon/1bpp_size_1x1.ico new file mode 100644 index 000000000..1161a3f3c --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_1x1.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f13d1bbfa5b29bc386270cb492b705eded0825a9eb3a6341f4ea2b3dbe085cd1 +size 78 diff --git a/tests/Images/Input/Icon/1bpp_size_256x256.ico b/tests/Images/Input/Icon/1bpp_size_256x256.ico new file mode 100644 index 000000000..d6524a31f --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_256x256.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1aa37daf2fd65b424c5e13e94bed165328e624584cc5664d73bb4030a1e1f12 +size 16454 diff --git a/tests/Images/Input/Icon/1bpp_size_2x2.ico b/tests/Images/Input/Icon/1bpp_size_2x2.ico new file mode 100644 index 000000000..73394156a --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_2x2.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18be2a5384812de1bec70733fb0b283a159edc5d0bc03981de8fb3ccddb8911e +size 86 diff --git a/tests/Images/Input/Icon/1bpp_size_31x31.ico b/tests/Images/Input/Icon/1bpp_size_31x31.ico new file mode 100644 index 000000000..8dffe659f --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_31x31.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3a4e7964e3ed5ca2a98929e2f903a6b969961410aab6a935a0c54fbe716d0c3 +size 318 diff --git a/tests/Images/Input/Icon/1bpp_size_32x32.ico b/tests/Images/Input/Icon/1bpp_size_32x32.ico new file mode 100644 index 000000000..e281eb378 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_32x32.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06a55f8219234e43beec6776fe15a7d6d4ad314deaf64df115ea45f2100e5283 +size 326 diff --git a/tests/Images/Input/Icon/1bpp_size_33x33.ico b/tests/Images/Input/Icon/1bpp_size_33x33.ico new file mode 100644 index 000000000..c5e4677d3 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_33x33.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82fa82f6b954515eace3cdd6d4681abd149b37354a7dee4f0d1966f516f27850 +size 598 diff --git a/tests/Images/Input/Icon/1bpp_size_3x3.ico b/tests/Images/Input/Icon/1bpp_size_3x3.ico new file mode 100644 index 000000000..89872b959 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_3x3.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ff0bf1c415925642d99691a9a9a6b92d9995447bc62ed80b9b0c6d4efdcd19b +size 94 diff --git a/tests/Images/Input/Icon/1bpp_size_4x4.ico b/tests/Images/Input/Icon/1bpp_size_4x4.ico new file mode 100644 index 000000000..1e47b4596 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_4x4.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5853ba73b06e0f2a0c71850011526419db3bd7c76e5b7b2f6b22f748ce919bf2 +size 102 diff --git a/tests/Images/Input/Icon/1bpp_size_5x5.ico b/tests/Images/Input/Icon/1bpp_size_5x5.ico new file mode 100644 index 000000000..5152c7575 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_5x5.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ecd253a6862ec9a9870c8a8a370528b28c22d23247dfc29e09fab65d95b9416d +size 110 diff --git a/tests/Images/Input/Icon/1bpp_size_6x6.ico b/tests/Images/Input/Icon/1bpp_size_6x6.ico new file mode 100644 index 000000000..a1d5c09c0 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_6x6.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a80c7f8d37dc3997bcd674a5af835cae802cacbf5d65020f0aaae67f70cfc31e +size 118 diff --git a/tests/Images/Input/Icon/1bpp_size_7x7.ico b/tests/Images/Input/Icon/1bpp_size_7x7.ico new file mode 100644 index 000000000..9c5a227e3 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_7x7.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:00a26274e8563e6378f8bfa4d1aa9695030593a38791ca366cecd3949b0f52af +size 126 diff --git a/tests/Images/Input/Icon/1bpp_size_8x8.ico b/tests/Images/Input/Icon/1bpp_size_8x8.ico new file mode 100644 index 000000000..c019914ee --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_8x8.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78150aee2f5d5ccd2a172abfe11f1127efb950cb9173e22e380809afb2a94d3c +size 134 diff --git a/tests/Images/Input/Icon/1bpp_size_9x9.ico b/tests/Images/Input/Icon/1bpp_size_9x9.ico new file mode 100644 index 000000000..2f3fd28eb --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_size_9x9.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8cecf833b31208710c1dde1bed3322a95453468a3f4b39afdad66ac9bc5f86b +size 142 diff --git a/tests/Images/Input/Icon/1bpp_transp_not_square.ico b/tests/Images/Input/Icon/1bpp_transp_not_square.ico new file mode 100644 index 000000000..1c678ec40 --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_transp_not_square.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd9f41711b53d4e1e915ddb992522b97a981fbe3f4536826f0c66e2d6a3677fb +size 182 diff --git a/tests/Images/Input/Icon/1bpp_transp_partial.ico b/tests/Images/Input/Icon/1bpp_transp_partial.ico new file mode 100644 index 000000000..6365a53df --- /dev/null +++ b/tests/Images/Input/Icon/1bpp_transp_partial.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cedcd221abf4b95115f9f8f34f15da456b09e7e56972cced24a99fa56bf8aca9 +size 326 diff --git a/tests/Images/Input/Icon/24bpp_size_15x15.ico b/tests/Images/Input/Icon/24bpp_size_15x15.ico new file mode 100644 index 000000000..f8697e2b5 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_15x15.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b506403852d936d14975ed9aba1c50ab3873cbbd81afcf38381f8e5e841fafd0 +size 842 diff --git a/tests/Images/Input/Icon/24bpp_size_16x16.ico b/tests/Images/Input/Icon/24bpp_size_16x16.ico new file mode 100644 index 000000000..e6de107d7 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_16x16.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8bfa9de0f613e9e82e9037193c4124f87d05ac1390459e2f018da45c16200de6 +size 894 diff --git a/tests/Images/Input/Icon/24bpp_size_17x17.ico b/tests/Images/Input/Icon/24bpp_size_17x17.ico new file mode 100644 index 000000000..2c37ffa8b --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_17x17.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a4cd2ad22e55000a706d365e0a3395f3e4d9a3933c00880d5f12903ac0aed60e +size 1014 diff --git a/tests/Images/Input/Icon/24bpp_size_1x1.ico b/tests/Images/Input/Icon/24bpp_size_1x1.ico new file mode 100644 index 000000000..f9137f61a --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_1x1.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b20e9a6479c3831b7af75d30bc909ce3046e45384bfd62d7a10ac6816c1c947 +size 70 diff --git a/tests/Images/Input/Icon/24bpp_size_256x256.ico b/tests/Images/Input/Icon/24bpp_size_256x256.ico new file mode 100644 index 000000000..08f928c8e --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_256x256.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1175641f39e68bd6cd38b8f64f02510b22c5a642afc7503d357b064163b3d37b +size 204862 diff --git a/tests/Images/Input/Icon/24bpp_size_2x2.ico b/tests/Images/Input/Icon/24bpp_size_2x2.ico new file mode 100644 index 000000000..d6d472a25 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_2x2.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72a1380a306b51ae37eabd3edeb0b134b0f8e8e120e6c64b6b08bd833a9c70a4 +size 86 diff --git a/tests/Images/Input/Icon/24bpp_size_31x31.ico b/tests/Images/Input/Icon/24bpp_size_31x31.ico new file mode 100644 index 000000000..5c585c582 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_31x31.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:574b0971908b44334f1f3be79e5b0ff336e74a8b9947b43a295d3a2b86733965 +size 3162 diff --git a/tests/Images/Input/Icon/24bpp_size_32x32.ico b/tests/Images/Input/Icon/24bpp_size_32x32.ico new file mode 100644 index 000000000..3663b8767 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_32x32.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa45880e5611436fc06de0603481d0a61044e52a1a053961fdda1139bb5660a6 +size 3262 diff --git a/tests/Images/Input/Icon/24bpp_size_33x33.ico b/tests/Images/Input/Icon/24bpp_size_33x33.ico new file mode 100644 index 000000000..4834b48c8 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_33x33.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8704a77d6264f6f104e23950099e3d3a4a577787e67c7c39d7925f9d0a347572 +size 3626 diff --git a/tests/Images/Input/Icon/24bpp_size_3x3.ico b/tests/Images/Input/Icon/24bpp_size_3x3.ico new file mode 100644 index 000000000..f2c11ccfe --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_3x3.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81229c27fbc684c0da99b1b04189046d5b9f531ee4111ccc43bde6404dce4f12 +size 110 diff --git a/tests/Images/Input/Icon/24bpp_size_4x4.ico b/tests/Images/Input/Icon/24bpp_size_4x4.ico new file mode 100644 index 000000000..2d7880a03 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_4x4.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7cb620eb83acbf20d308f5c6cb8b0246b8b156cdcc43e39f5809754fd4d5ddb +size 126 diff --git a/tests/Images/Input/Icon/24bpp_size_5x5.ico b/tests/Images/Input/Icon/24bpp_size_5x5.ico new file mode 100644 index 000000000..a98c85c19 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_5x5.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bdf1718e3d9695cdf2552e42219d1e3166ad5a94f53cbb44db4a7222e2a32f9a +size 162 diff --git a/tests/Images/Input/Icon/24bpp_size_6x6.ico b/tests/Images/Input/Icon/24bpp_size_6x6.ico new file mode 100644 index 000000000..5dd3c57c2 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_6x6.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1e5f3a8f59e297cf67251a89558d4406c4c515c3e3ce7555df4a53ef5420fc38 +size 206 diff --git a/tests/Images/Input/Icon/24bpp_size_7x7.ico b/tests/Images/Input/Icon/24bpp_size_7x7.ico new file mode 100644 index 000000000..d9622629e --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_7x7.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ee1b5836c9f8c89e9b8c8873f1ad92f7555ed9706f4dc76345a727bf3e9f334 +size 258 diff --git a/tests/Images/Input/Icon/24bpp_size_8x8.ico b/tests/Images/Input/Icon/24bpp_size_8x8.ico new file mode 100644 index 000000000..39be58ce4 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_8x8.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b46ca32ddb84074d9140224738480eaa0a6c0dce2dbf2074625add1901c27117 +size 286 diff --git a/tests/Images/Input/Icon/24bpp_size_9x9.ico b/tests/Images/Input/Icon/24bpp_size_9x9.ico new file mode 100644 index 000000000..9e7873eaf --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_size_9x9.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7454d6332f4bdba929d610e4a8a232b1443d5a64119de4e69c00e0f03e55e237 +size 350 diff --git a/tests/Images/Input/Icon/24bpp_transp.ico b/tests/Images/Input/Icon/24bpp_transp.ico new file mode 100644 index 000000000..a64157a63 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_transp.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb8ea41822350e5f40bac2aef19ec7a4c40561ce6637948b3fa6db7835c1fded +size 3262 diff --git a/tests/Images/Input/Icon/24bpp_transp_not_square.ico b/tests/Images/Input/Icon/24bpp_transp_not_square.ico new file mode 100644 index 000000000..5abf2ad66 --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_transp_not_square.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:44b4c79ff497df0e99f55d68d82f59c0d7c2f4d5e9bb63bcc1b5910f4a2853db +size 1126 diff --git a/tests/Images/Input/Icon/24bpp_transp_partial.ico b/tests/Images/Input/Icon/24bpp_transp_partial.ico new file mode 100644 index 000000000..d1a37498b --- /dev/null +++ b/tests/Images/Input/Icon/24bpp_transp_partial.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f8d109413d7f699b92e9d6a5e2c52d2b5c747f2ca9ff31d326f8d4ec2fd5840f +size 3262 diff --git a/tests/Images/Input/Icon/32bpp_size_15x15.ico b/tests/Images/Input/Icon/32bpp_size_15x15.ico new file mode 100644 index 000000000..a7f94e94d --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_15x15.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02b5abd8e15ef2fba1a26cdbdf6e8f66abbbf9aa188404ef911a1d2d02b7b050 +size 1022 diff --git a/tests/Images/Input/Icon/32bpp_size_16x16.ico b/tests/Images/Input/Icon/32bpp_size_16x16.ico new file mode 100644 index 000000000..609a51826 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_16x16.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50bb07bd12f9b388ba6a3abbb815aaf1c800438e35ec48201269fa23342e5622 +size 1150 diff --git a/tests/Images/Input/Icon/32bpp_size_17x17.ico b/tests/Images/Input/Icon/32bpp_size_17x17.ico new file mode 100644 index 000000000..13a71140f --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_17x17.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6bf4c701d38fc988186e3fcfee6e426ed5a84807b54b91e4ab8c1b12e0794746 +size 1286 diff --git a/tests/Images/Input/Icon/32bpp_size_1x1.ico b/tests/Images/Input/Icon/32bpp_size_1x1.ico new file mode 100644 index 000000000..3f449eefe --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_1x1.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f62be892b36609a57c34eb4784dfb6dc82698ecd15709898a6579ee4f21f668e +size 70 diff --git a/tests/Images/Input/Icon/32bpp_size_256x256.ico b/tests/Images/Input/Icon/32bpp_size_256x256.ico new file mode 100644 index 000000000..2229aee95 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_256x256.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b15f75adc54e70c4751cf9973854f225ef15b12bdaddb5ab00cb9edfd8b386b1 +size 270398 diff --git a/tests/Images/Input/Icon/32bpp_size_2x2.ico b/tests/Images/Input/Icon/32bpp_size_2x2.ico new file mode 100644 index 000000000..cbb64292b --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_2x2.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa859752136cdf055874ae71589f9585aaaeaef804b13ce192991793d9e57e57 +size 86 diff --git a/tests/Images/Input/Icon/32bpp_size_31x31.ico b/tests/Images/Input/Icon/32bpp_size_31x31.ico new file mode 100644 index 000000000..837e8f512 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_31x31.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5ecf1dc1fdd3dc6cb2fd90b49632b19260e73ad3a6624372d1e9eefc470ed6b +size 4030 diff --git a/tests/Images/Input/Icon/32bpp_size_32x32.ico b/tests/Images/Input/Icon/32bpp_size_32x32.ico new file mode 100644 index 000000000..b359d4d84 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_32x32.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:395ccdc596487ed63db4d893d03b6aa24743b37279d896196d8d38e7028415ea +size 4286 diff --git a/tests/Images/Input/Icon/32bpp_size_33x33.ico b/tests/Images/Input/Icon/32bpp_size_33x33.ico new file mode 100644 index 000000000..01df9ae7d --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_33x33.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7e8b89c061abcf959b90149585cc34237aad19e1f67c162d62e5adbad4826970 +size 4682 diff --git a/tests/Images/Input/Icon/32bpp_size_3x3.ico b/tests/Images/Input/Icon/32bpp_size_3x3.ico new file mode 100644 index 000000000..8879d3f1f --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_3x3.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52b7772a9c8fae189abc3cf37d5d24bcdfe94077e6b1cf7be8cc7e0bc6f6bddf +size 110 diff --git a/tests/Images/Input/Icon/32bpp_size_4x4.ico b/tests/Images/Input/Icon/32bpp_size_4x4.ico new file mode 100644 index 000000000..d800b2198 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_4x4.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2bb0831fab10fb0ff9a0b2b2ea137609a45a6ec3982d594878996eafc32836ad +size 142 diff --git a/tests/Images/Input/Icon/32bpp_size_5x5.ico b/tests/Images/Input/Icon/32bpp_size_5x5.ico new file mode 100644 index 000000000..710c38a23 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_5x5.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10e2ed5cbacc761f2d467c22a8d72dcc4086b9423c5487ba05826804c642730a +size 182 diff --git a/tests/Images/Input/Icon/32bpp_size_6x6.ico b/tests/Images/Input/Icon/32bpp_size_6x6.ico new file mode 100644 index 000000000..4223c1fc2 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_6x6.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ab79a308d24592557b368f00566999d777e38de385eb6ebabc90d53ac723ae9 +size 230 diff --git a/tests/Images/Input/Icon/32bpp_size_7x7.ico b/tests/Images/Input/Icon/32bpp_size_7x7.ico new file mode 100644 index 000000000..1e321acb6 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_7x7.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3799164811b0284535fa90ca60f13e9c0754ca4e8f00d96a83951e91e748d760 +size 286 diff --git a/tests/Images/Input/Icon/32bpp_size_8x8.ico b/tests/Images/Input/Icon/32bpp_size_8x8.ico new file mode 100644 index 000000000..b44fe22d5 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_8x8.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a3187750c9313024f983fdfca270f5db2c5d730831adfce0fb19ddac25deecc +size 350 diff --git a/tests/Images/Input/Icon/32bpp_size_9x9.ico b/tests/Images/Input/Icon/32bpp_size_9x9.ico new file mode 100644 index 000000000..682b148ed --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_size_9x9.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ab8bd47cc2d2ce0e9c1a5810b5ccbe3f46e35001114c18f34cbf51ff0566bf6 +size 422 diff --git a/tests/Images/Input/Icon/32bpp_transp.ico b/tests/Images/Input/Icon/32bpp_transp.ico new file mode 100644 index 000000000..592536290 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_transp.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b4f94718304fa41041b8cebad7b76762d410b366b46d620f5599b03a2fa7ba00 +size 4286 diff --git a/tests/Images/Input/Icon/32bpp_transp_not_square.ico b/tests/Images/Input/Icon/32bpp_transp_not_square.ico new file mode 100644 index 000000000..3a0bb3dd0 --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_transp_not_square.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e3c2061b64df989e8ae68aee993eba5e11d03e23f282f063cc3929ec3ef2b0c +size 1462 diff --git a/tests/Images/Input/Icon/32bpp_transp_partial.ico b/tests/Images/Input/Icon/32bpp_transp_partial.ico new file mode 100644 index 000000000..334a0b75c --- /dev/null +++ b/tests/Images/Input/Icon/32bpp_transp_partial.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5bd34021ec302f203c39d038854607d9fe8bcd3133ea57b65ebc6da81aa8a4b +size 4286 diff --git a/tests/Images/Input/Icon/4bpp_size_15x15.ico b/tests/Images/Input/Icon/4bpp_size_15x15.ico new file mode 100644 index 000000000..ce67e54cc --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_15x15.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd1fd73648c1b7acc4aef4e0c4e6672ab7241e47cb52345c4459aa86185f4dfd +size 306 diff --git a/tests/Images/Input/Icon/4bpp_size_16x16.ico b/tests/Images/Input/Icon/4bpp_size_16x16.ico new file mode 100644 index 000000000..f26d88b36 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_16x16.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e5f8547e5e6aae3a2f662fa266d0f78731d310fb051f99dce5693d6adcbfbb4f +size 318 diff --git a/tests/Images/Input/Icon/4bpp_size_17x17.ico b/tests/Images/Input/Icon/4bpp_size_17x17.ico new file mode 100644 index 000000000..aa44dd4f1 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_17x17.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1fd0286a127371d4b40a5c30c6b221753a711231e386b636fcb07d2f1f92967f +size 398 diff --git a/tests/Images/Input/Icon/4bpp_size_1x1.ico b/tests/Images/Input/Icon/4bpp_size_1x1.ico new file mode 100644 index 000000000..7049b0b36 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_1x1.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:313f969c26d12cae6e778563d1c6c9df248c5d28ff657ad5054a280e06573106 +size 134 diff --git a/tests/Images/Input/Icon/4bpp_size_256x256.ico b/tests/Images/Input/Icon/4bpp_size_256x256.ico new file mode 100644 index 000000000..fa0740065 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_256x256.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcad369c1e56dd01b8644a5903ad25af19cc78047b5e0821546d1171a0ab31ff +size 41086 diff --git a/tests/Images/Input/Icon/4bpp_size_2x2.ico b/tests/Images/Input/Icon/4bpp_size_2x2.ico new file mode 100644 index 000000000..2b8d74afa --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_2x2.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:11b1142401678412ce8718dd6e943fab05ec7974c7ae36286316e7f4d168f0f5 +size 142 diff --git a/tests/Images/Input/Icon/4bpp_size_31x31.ico b/tests/Images/Input/Icon/4bpp_size_31x31.ico new file mode 100644 index 000000000..86b71f36c --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_31x31.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2b19f60f3441b268a19be0f99078d079c4eb416b882118071ad43ceff41ca40b +size 746 diff --git a/tests/Images/Input/Icon/4bpp_size_32x32.ico b/tests/Images/Input/Icon/4bpp_size_32x32.ico new file mode 100644 index 000000000..aa8fc0932 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_32x32.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f0863b486575dca2d5e3ce07da10cb30e039524f17026d9668d75bad780e833 +size 766 diff --git a/tests/Images/Input/Icon/4bpp_size_33x33.ico b/tests/Images/Input/Icon/4bpp_size_33x33.ico new file mode 100644 index 000000000..ad93685f2 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_33x33.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64b77f80fb48237a0f9cfbd808289e53ed7a70b107f190c93d76d32342829ccb +size 1050 diff --git a/tests/Images/Input/Icon/4bpp_size_3x3.ico b/tests/Images/Input/Icon/4bpp_size_3x3.ico new file mode 100644 index 000000000..781266a67 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_3x3.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f8cb358defec66494d3be4eb01fd7e304edfd04435b4552d51769d0858659686 +size 150 diff --git a/tests/Images/Input/Icon/4bpp_size_4x4.ico b/tests/Images/Input/Icon/4bpp_size_4x4.ico new file mode 100644 index 000000000..ffe599149 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_4x4.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53083ae340dcc04d88acaf9d75a2dc68b23500c294eb3ed4e15e3fba86c5843f +size 158 diff --git a/tests/Images/Input/Icon/4bpp_size_5x5.ico b/tests/Images/Input/Icon/4bpp_size_5x5.ico new file mode 100644 index 000000000..70f2db036 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_5x5.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4f1401d87e285b18e6f818dfb00a3fb275e72e2fd4bce6652bfdd74bf9565f3 +size 166 diff --git a/tests/Images/Input/Icon/4bpp_size_6x6.ico b/tests/Images/Input/Icon/4bpp_size_6x6.ico new file mode 100644 index 000000000..230d8e85f --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_6x6.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3fa6bdd6125a9de6d3ddaafd5e62e0435b14c52f82239a6d36d44aaf5a49361 +size 174 diff --git a/tests/Images/Input/Icon/4bpp_size_7x7.ico b/tests/Images/Input/Icon/4bpp_size_7x7.ico new file mode 100644 index 000000000..7c4b9834b --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_7x7.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c4025a4bd2dde619f950f69d38e042ae38d2a1840d7b00540c372546b5d8ccb0 +size 182 diff --git a/tests/Images/Input/Icon/4bpp_size_8x8.ico b/tests/Images/Input/Icon/4bpp_size_8x8.ico new file mode 100644 index 000000000..b1f3050bf --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_8x8.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:332563676808485aac8e2635baacad3f4d1ce5fc64c6be07daa25962f4fa1687 +size 190 diff --git a/tests/Images/Input/Icon/4bpp_size_9x9.ico b/tests/Images/Input/Icon/4bpp_size_9x9.ico new file mode 100644 index 000000000..fc7751086 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_size_9x9.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da5169395108194503853db2f431e0d02b98a191a1bf597c31fe269e7d84206a +size 234 diff --git a/tests/Images/Input/Icon/4bpp_transp_not_square.ico b/tests/Images/Input/Icon/4bpp_transp_not_square.ico new file mode 100644 index 000000000..6b2babe8e --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_transp_not_square.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2dede473b7a427029cdb319ecf04aaf71cf8ce1d806efb65d7e3844ebd1703f9 +size 350 diff --git a/tests/Images/Input/Icon/4bpp_transp_partial.ico b/tests/Images/Input/Icon/4bpp_transp_partial.ico new file mode 100644 index 000000000..4394b9e43 --- /dev/null +++ b/tests/Images/Input/Icon/4bpp_transp_partial.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7e0617f49eb057ab0346203e0cd04fd4e93de71053bbbfaa3c9655696b10a80d +size 766 diff --git a/tests/Images/Input/Icon/8bpp_size_15x15.ico b/tests/Images/Input/Icon/8bpp_size_15x15.ico new file mode 100644 index 000000000..086edb745 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_15x15.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:577c86aa8b78cca86885ed20b702b260150eacf738beb18da28c25bcb01cfdcd +size 1386 diff --git a/tests/Images/Input/Icon/8bpp_size_16x16.ico b/tests/Images/Input/Icon/8bpp_size_16x16.ico new file mode 100644 index 000000000..e09d74447 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_16x16.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9900b6783aac800836e19890f11fda1bf1d2138dee63403adbc79bf207a71dfd +size 1406 diff --git a/tests/Images/Input/Icon/8bpp_size_17x17.ico b/tests/Images/Input/Icon/8bpp_size_17x17.ico new file mode 100644 index 000000000..9a5684b4d --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_17x17.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b1c214eb0fd9f5b1f1da4ceeb98a3cc0d7b6b02d7ed6aaa5138078e48bf8613 +size 1494 diff --git a/tests/Images/Input/Icon/8bpp_size_1x1.ico b/tests/Images/Input/Icon/8bpp_size_1x1.ico new file mode 100644 index 000000000..20961847a --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_1x1.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:032c11a8e5aaa5f9beb997c83504233b5ad278d3a4e129cd384a4ca62950a998 +size 1094 diff --git a/tests/Images/Input/Icon/8bpp_size_256x256.ico b/tests/Images/Input/Icon/8bpp_size_256x256.ico new file mode 100644 index 000000000..59b0bb63b --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_256x256.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7aa8f4fc33c541f88318d7c36a5b5e964cb0470c541677790b49b85638c63fd6 +size 74814 diff --git a/tests/Images/Input/Icon/8bpp_size_2x2.ico b/tests/Images/Input/Icon/8bpp_size_2x2.ico new file mode 100644 index 000000000..bcbdf9c07 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_2x2.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e1c2b72b6ddf0cfedd9e20e5abf3ae3fd19066be811251bb9db404e6dabe279 +size 1102 diff --git a/tests/Images/Input/Icon/8bpp_size_31x31.ico b/tests/Images/Input/Icon/8bpp_size_31x31.ico new file mode 100644 index 000000000..7c320d183 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_31x31.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a588e6f1031c1c8f0d66b9eef770d01b3f79aaea4203d58ee07255c1c6ee258 +size 2238 diff --git a/tests/Images/Input/Icon/8bpp_size_32x32.ico b/tests/Images/Input/Icon/8bpp_size_32x32.ico new file mode 100644 index 000000000..af7581f35 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_32x32.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b00ab33cbb42e9f499844e5add713fcc83854cc2cb104d5c9bd04a456662099 +size 2238 diff --git a/tests/Images/Input/Icon/8bpp_size_33x33.ico b/tests/Images/Input/Icon/8bpp_size_33x33.ico new file mode 100644 index 000000000..3c8043fe6 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_33x33.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e356bb9200375f8fcbc5a0ed70353194a7b1432133e0c61f0b2f03bca3888080 +size 2538 diff --git a/tests/Images/Input/Icon/8bpp_size_3x3.ico b/tests/Images/Input/Icon/8bpp_size_3x3.ico new file mode 100644 index 000000000..075791998 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_3x3.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13cccd62e97bbe6cdd6ca5c4bc765794c7babd3638ff4d09b116a62c3c50be56 +size 1110 diff --git a/tests/Images/Input/Icon/8bpp_size_4x4.ico b/tests/Images/Input/Icon/8bpp_size_4x4.ico new file mode 100644 index 000000000..af95b287d --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_4x4.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4343544be11a37083219d99d2dfb4a16b02c309d54223897d6c71503414cc2ef +size 1118 diff --git a/tests/Images/Input/Icon/8bpp_size_5x5.ico b/tests/Images/Input/Icon/8bpp_size_5x5.ico new file mode 100644 index 000000000..2e4f80495 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_5x5.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b7cfdf4a0d696573a4cb5291e3165c98444692f1d03a3cab1630df33c765ecd9 +size 1146 diff --git a/tests/Images/Input/Icon/8bpp_size_6x6.ico b/tests/Images/Input/Icon/8bpp_size_6x6.ico new file mode 100644 index 000000000..65ca70be2 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_6x6.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8d36eb593ee34d22e5ff99a08f2b177dbb92355f85210b2a9e53d7ce9e2cc6b +size 1158 diff --git a/tests/Images/Input/Icon/8bpp_size_7x7.ico b/tests/Images/Input/Icon/8bpp_size_7x7.ico new file mode 100644 index 000000000..e02e8fb37 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_7x7.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29050464e324a70bb8a41fb5732b96dbf3bcb36a74ca29775e1f37b07b9ee582 +size 1170 diff --git a/tests/Images/Input/Icon/8bpp_size_8x8.ico b/tests/Images/Input/Icon/8bpp_size_8x8.ico new file mode 100644 index 000000000..39be58ce4 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_8x8.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b46ca32ddb84074d9140224738480eaa0a6c0dce2dbf2074625add1901c27117 +size 286 diff --git a/tests/Images/Input/Icon/8bpp_size_9x9.ico b/tests/Images/Input/Icon/8bpp_size_9x9.ico new file mode 100644 index 000000000..8819490ae --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_size_9x9.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4ddeaa63b1ec9d389c140499e2b5d2e911a57d8f37467696bb9e9ec3e60ec77b +size 1230 diff --git a/tests/Images/Input/Icon/8bpp_transp_not_square.ico b/tests/Images/Input/Icon/8bpp_transp_not_square.ico new file mode 100644 index 000000000..6b6bbdc9f --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_transp_not_square.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04a255b4cd43ec7005a9a946c2b3dba57eba709b16be85ef77e553943d35f745 +size 1478 diff --git a/tests/Images/Input/Icon/8bpp_transp_partial.ico b/tests/Images/Input/Icon/8bpp_transp_partial.ico new file mode 100644 index 000000000..73cd34c73 --- /dev/null +++ b/tests/Images/Input/Icon/8bpp_transp_partial.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:20abca3096b955bc91ed95d194ff3f856473d6d372b5bea0d8c75fd20a231a26 +size 2238 diff --git a/tests/Images/Input/Icon/cur_fake.ico b/tests/Images/Input/Icon/cur_fake.ico new file mode 100644 index 000000000..cad7542c8 --- /dev/null +++ b/tests/Images/Input/Icon/cur_fake.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6679016e7954e335cef537630d122cc3a7a05cb2f3ef32f72811d724b83d4c28 +size 4286 diff --git a/tests/Images/Input/Icon/cur_real.cur b/tests/Images/Input/Icon/cur_real.cur new file mode 100644 index 000000000..cad7542c8 --- /dev/null +++ b/tests/Images/Input/Icon/cur_real.cur @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6679016e7954e335cef537630d122cc3a7a05cb2f3ef32f72811d724b83d4c28 +size 4286 diff --git a/tests/Images/Input/Icon/ico_fake.cur b/tests/Images/Input/Icon/ico_fake.cur new file mode 100644 index 000000000..5ab040538 --- /dev/null +++ b/tests/Images/Input/Icon/ico_fake.cur @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d3a3185dcf0a2c3f5d3fe821474f6787c4de7cffe08db6bd730073ad94e7538 +size 4286 diff --git a/tests/Images/Input/Icon/invalid_RLE4.ico b/tests/Images/Input/Icon/invalid_RLE4.ico new file mode 100644 index 000000000..5d2e25fd3 --- /dev/null +++ b/tests/Images/Input/Icon/invalid_RLE4.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:43c543a1c66608a89f0b187afa1526af4be4f7c94265897002b3a150328b964e +size 86 diff --git a/tests/Images/Input/Icon/invalid_RLE8.ico b/tests/Images/Input/Icon/invalid_RLE8.ico new file mode 100644 index 000000000..2ebefbf33 --- /dev/null +++ b/tests/Images/Input/Icon/invalid_RLE8.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b55b3f3f87d9d5801150ffd999c62623528a33d031ca8d6fe665be3328d8c94d +size 86 diff --git a/tests/Images/Input/Icon/invalid_all.ico b/tests/Images/Input/Icon/invalid_all.ico new file mode 100644 index 000000000..ca34a2578 --- /dev/null +++ b/tests/Images/Input/Icon/invalid_all.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93c4f059ced481667315dd7b90e9c1beed0a42a08f3ff8e51e2388919fafa79a +size 283 diff --git a/tests/Images/Input/Icon/invalid_bpp.ico b/tests/Images/Input/Icon/invalid_bpp.ico new file mode 100644 index 000000000..913f780ed --- /dev/null +++ b/tests/Images/Input/Icon/invalid_bpp.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:396bfd08531fc0eae8b5e364ef4c62035e8493a3f59286dedbca6f441d8e9690 +size 86 diff --git a/tests/Images/Input/Icon/invalid_compression.ico b/tests/Images/Input/Icon/invalid_compression.ico new file mode 100644 index 000000000..7e697d3d2 --- /dev/null +++ b/tests/Images/Input/Icon/invalid_compression.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f051ed80db684748d2b2669c77b95068e209b2fe2917fa65f6218beef4dcead5 +size 830 diff --git a/tests/Images/Input/Icon/invalid_png.ico b/tests/Images/Input/Icon/invalid_png.ico new file mode 100644 index 000000000..cbd394fc6 --- /dev/null +++ b/tests/Images/Input/Icon/invalid_png.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61bc9e74d8fd9f72c8ccaf9a3887c517e17c0a39d9d41acabc3699be545b9703 +size 901 diff --git a/tests/Images/Input/Icon/mixed_bmp_png_a.ico b/tests/Images/Input/Icon/mixed_bmp_png_a.ico new file mode 100644 index 000000000..f35027255 --- /dev/null +++ b/tests/Images/Input/Icon/mixed_bmp_png_a.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:47adfa4e36adf74ae49cfa481cb54cffe659c09d4b52765e973b6adc8cc31e97 +size 3653 diff --git a/tests/Images/Input/Icon/mixed_bmp_png_b.ico b/tests/Images/Input/Icon/mixed_bmp_png_b.ico new file mode 100644 index 000000000..3efdcab74 --- /dev/null +++ b/tests/Images/Input/Icon/mixed_bmp_png_b.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96338768d8f9c90a6af94268dc55d94809a412c3164c381926b3759ffbf2df79 +size 45693 diff --git a/tests/Images/Input/Icon/mixed_bmp_png_c.ico b/tests/Images/Input/Icon/mixed_bmp_png_c.ico new file mode 100644 index 000000000..65b504eef --- /dev/null +++ b/tests/Images/Input/Icon/mixed_bmp_png_c.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9be0358616581f45eddeaee474c0ce0be8a82279428f5ef1890b2fb6f0c0d27 +size 164189 diff --git a/tests/Images/Input/Icon/multi_size_a.ico b/tests/Images/Input/Icon/multi_size_a.ico new file mode 100644 index 000000000..c34fdc638 --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_a.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dba75ec62f5785ce5d16c2c5c04637b18ccb164917092373b3d470326e7bc0c4 +size 17542 diff --git a/tests/Images/Input/Icon/multi_size_b.ico b/tests/Images/Input/Icon/multi_size_b.ico new file mode 100644 index 000000000..2065bd638 --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_b.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d7ac6313ee263103f4ab6aa5147b1d85bf5ff792c0980189aac9bfab1288011 +size 99678 diff --git a/tests/Images/Input/Icon/multi_size_c.ico b/tests/Images/Input/Icon/multi_size_c.ico new file mode 100644 index 000000000..c6ee58297 --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_c.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85e04b807e084bc9a0e1a65289e21ca1baac1e70c3a37fabbea7d69995945f08 +size 202850 diff --git a/tests/Images/Input/Icon/multi_size_d.ico b/tests/Images/Input/Icon/multi_size_d.ico new file mode 100644 index 000000000..3d9fc96fb --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_d.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a0ce27b63b386c4fdbf7835db738f0e98f274f5a112b7bd45c32dfab93952a0 +size 216804 diff --git a/tests/Images/Input/Icon/multi_size_e.ico b/tests/Images/Input/Icon/multi_size_e.ico new file mode 100644 index 000000000..8f2991acb --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_e.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:363ff3655e978ffe30a8cefec3bd4202a1ae0a22f3ab48e56362f56a31fb349f +size 372526 diff --git a/tests/Images/Input/Icon/multi_size_f.ico b/tests/Images/Input/Icon/multi_size_f.ico new file mode 100644 index 000000000..99948cf1e --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_f.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04701ce87eb82280a4e53d816a0ac3ee91ebc28b1959641bddb90787015ff4a8 +size 3084 diff --git a/tests/Images/Input/Icon/multi_size_multi_bits_a.ico b/tests/Images/Input/Icon/multi_size_multi_bits_a.ico new file mode 100644 index 000000000..12b2bf66c --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_multi_bits_a.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a1561795509c9e8dbf0633db9b4c242e72e0ebe4ae9a7718328e96b6b273c3ca +size 4710 diff --git a/tests/Images/Input/Icon/multi_size_multi_bits_b.ico b/tests/Images/Input/Icon/multi_size_multi_bits_b.ico new file mode 100644 index 000000000..599168aea --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_multi_bits_b.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8fe15c1a8ca4bae0ad588863418af960fb62def2db4abfcae594de4fc1b2304a +size 31134 diff --git a/tests/Images/Input/Icon/multi_size_multi_bits_c.ico b/tests/Images/Input/Icon/multi_size_multi_bits_c.ico new file mode 100644 index 000000000..701b574dc --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_multi_bits_c.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c5afa3da7d278c1d2272581971117408c38ec5fe3aaac17e5234f7a8012fd9e2 +size 72513 diff --git a/tests/Images/Input/Icon/multi_size_multi_bits_d.ico b/tests/Images/Input/Icon/multi_size_multi_bits_d.ico new file mode 100644 index 000000000..271ec92a5 --- /dev/null +++ b/tests/Images/Input/Icon/multi_size_multi_bits_d.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e0754e570ab3a2ce81759aa206fc6e8780fb1024fb20e60dbdb329ee4c0c1831 +size 293950 From bad83f9a9018f0a2021e5d06b360b8484be16b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Tue, 18 Jun 2024 21:29:23 +0800 Subject: [PATCH 70/79] Update identifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/Ico/IcoDecoderTests.cs | 222 +++++++++--------- tests/ImageSharp.Tests/TestImages.cs | 222 +++++++++--------- 2 files changed, 222 insertions(+), 222 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index ab4ec0a3d..8ea7cf6d4 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -24,118 +24,118 @@ public class IcoDecoderTests } [Theory] - [WithFile(Bpp1_size_15x15, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_16x16, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_17x17, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_1x1, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_256x256, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_2x2, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_31x31, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_32x32, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_33x33, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_3x3, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_4x4, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_5x5, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_6x6, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_7x7, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_8x8, PixelTypes.Rgba32)] - [WithFile(Bpp1_size_9x9, PixelTypes.Rgba32)] - [WithFile(Bpp1_transp_not_square, PixelTypes.Rgba32)] - [WithFile(Bpp1_transp_partial, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_15x15, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_16x16, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_17x17, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_1x1, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_256x256, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_2x2, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_31x31, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_32x32, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_33x33, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_3x3, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_4x4, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_5x5, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_6x6, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_7x7, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_8x8, PixelTypes.Rgba32)] - [WithFile(Bpp24_size_9x9, PixelTypes.Rgba32)] - [WithFile(Bpp24_transp_not_square, PixelTypes.Rgba32)] - [WithFile(Bpp24_transp_partial, PixelTypes.Rgba32)] - [WithFile(Bpp24_transp, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_15x15, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_16x16, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_17x17, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_1x1, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_256x256, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_2x2, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_31x31, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_32x32, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_33x33, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_3x3, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_4x4, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_5x5, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_6x6, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_7x7, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_8x8, PixelTypes.Rgba32)] - [WithFile(Bpp32_size_9x9, PixelTypes.Rgba32)] - [WithFile(Bpp32_transp_not_square, PixelTypes.Rgba32)] - [WithFile(Bpp32_transp_partial, PixelTypes.Rgba32)] - [WithFile(Bpp32_transp, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_15x15, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_16x16, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_17x17, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_1x1, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_256x256, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_2x2, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_31x31, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_32x32, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_33x33, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_3x3, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_4x4, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_5x5, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_6x6, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_7x7, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_8x8, PixelTypes.Rgba32)] - [WithFile(Bpp4_size_9x9, PixelTypes.Rgba32)] - [WithFile(Bpp4_transp_not_square, PixelTypes.Rgba32)] - [WithFile(Bpp4_transp_partial, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_15x15, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_16x16, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_17x17, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_1x1, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_256x256, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_2x2, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_31x31, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_32x32, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_33x33, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_3x3, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_4x4, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_5x5, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_6x6, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_7x7, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_8x8, PixelTypes.Rgba32)] - [WithFile(Bpp8_size_9x9, PixelTypes.Rgba32)] - [WithFile(Bpp8_transp_not_square, PixelTypes.Rgba32)] - [WithFile(Bpp8_transp_partial, PixelTypes.Rgba32)] + [WithFile(Bpp1Size15x15, PixelTypes.Rgba32)] + [WithFile(Bpp1Size16x16, PixelTypes.Rgba32)] + [WithFile(Bpp1Size17x17, PixelTypes.Rgba32)] + [WithFile(Bpp1Size1x1, PixelTypes.Rgba32)] + [WithFile(Bpp1Size256x256, PixelTypes.Rgba32)] + [WithFile(Bpp1Size2x2, PixelTypes.Rgba32)] + [WithFile(Bpp1Size31x31, PixelTypes.Rgba32)] + [WithFile(Bpp1Size32x32, PixelTypes.Rgba32)] + [WithFile(Bpp1Size33x33, PixelTypes.Rgba32)] + [WithFile(Bpp1Size3x3, PixelTypes.Rgba32)] + [WithFile(Bpp1Size4x4, PixelTypes.Rgba32)] + [WithFile(Bpp1Size5x5, PixelTypes.Rgba32)] + [WithFile(Bpp1Size6x6, PixelTypes.Rgba32)] + [WithFile(Bpp1Size7x7, PixelTypes.Rgba32)] + [WithFile(Bpp1Size8x8, PixelTypes.Rgba32)] + [WithFile(Bpp1Size9x9, PixelTypes.Rgba32)] + [WithFile(Bpp1TranspNotSquare, PixelTypes.Rgba32)] + [WithFile(Bpp1TranspPartial, PixelTypes.Rgba32)] + [WithFile(Bpp24Size15x15, PixelTypes.Rgba32)] + [WithFile(Bpp24Size16x16, PixelTypes.Rgba32)] + [WithFile(Bpp24Size17x17, PixelTypes.Rgba32)] + [WithFile(Bpp24Size1x1, PixelTypes.Rgba32)] + [WithFile(Bpp24Size256x256, PixelTypes.Rgba32)] + [WithFile(Bpp24Size2x2, PixelTypes.Rgba32)] + [WithFile(Bpp24Size31x31, PixelTypes.Rgba32)] + [WithFile(Bpp24Size32x32, PixelTypes.Rgba32)] + [WithFile(Bpp24Size33x33, PixelTypes.Rgba32)] + [WithFile(Bpp24Size3x3, PixelTypes.Rgba32)] + [WithFile(Bpp24Size4x4, PixelTypes.Rgba32)] + [WithFile(Bpp24Size5x5, PixelTypes.Rgba32)] + [WithFile(Bpp24Size6x6, PixelTypes.Rgba32)] + [WithFile(Bpp24Size7x7, PixelTypes.Rgba32)] + [WithFile(Bpp24Size8x8, PixelTypes.Rgba32)] + [WithFile(Bpp24Size9x9, PixelTypes.Rgba32)] + [WithFile(Bpp24TranspNotSquare, PixelTypes.Rgba32)] + [WithFile(Bpp24TranspPartial, PixelTypes.Rgba32)] + [WithFile(Bpp24Transp, PixelTypes.Rgba32)] + [WithFile(Bpp32Size15x15, PixelTypes.Rgba32)] + [WithFile(Bpp32Size16x16, PixelTypes.Rgba32)] + [WithFile(Bpp32Size17x17, PixelTypes.Rgba32)] + [WithFile(Bpp32Size1x1, PixelTypes.Rgba32)] + [WithFile(Bpp32Size256x256, PixelTypes.Rgba32)] + [WithFile(Bpp32Size2x2, PixelTypes.Rgba32)] + [WithFile(Bpp32Size31x31, PixelTypes.Rgba32)] + [WithFile(Bpp32Size32x32, PixelTypes.Rgba32)] + [WithFile(Bpp32Size33x33, PixelTypes.Rgba32)] + [WithFile(Bpp32Size3x3, PixelTypes.Rgba32)] + [WithFile(Bpp32Size4x4, PixelTypes.Rgba32)] + [WithFile(Bpp32Size5x5, PixelTypes.Rgba32)] + [WithFile(Bpp32Size6x6, PixelTypes.Rgba32)] + [WithFile(Bpp32Size7x7, PixelTypes.Rgba32)] + [WithFile(Bpp32Size8x8, PixelTypes.Rgba32)] + [WithFile(Bpp32Size9x9, PixelTypes.Rgba32)] + [WithFile(Bpp32TranspNotSquare, PixelTypes.Rgba32)] + [WithFile(Bpp32TranspPartial, PixelTypes.Rgba32)] + [WithFile(Bpp32Transp, PixelTypes.Rgba32)] + [WithFile(Bpp4Size15x15, PixelTypes.Rgba32)] + [WithFile(Bpp4Size16x16, PixelTypes.Rgba32)] + [WithFile(Bpp4Size17x17, PixelTypes.Rgba32)] + [WithFile(Bpp4Size1x1, PixelTypes.Rgba32)] + [WithFile(Bpp4Size256x256, PixelTypes.Rgba32)] + [WithFile(Bpp4Size2x2, PixelTypes.Rgba32)] + [WithFile(Bpp4Size31x31, PixelTypes.Rgba32)] + [WithFile(Bpp4Size32x32, PixelTypes.Rgba32)] + [WithFile(Bpp4Size33x33, PixelTypes.Rgba32)] + [WithFile(Bpp4Size3x3, PixelTypes.Rgba32)] + [WithFile(Bpp4Size4x4, PixelTypes.Rgba32)] + [WithFile(Bpp4Size5x5, PixelTypes.Rgba32)] + [WithFile(Bpp4Size6x6, PixelTypes.Rgba32)] + [WithFile(Bpp4Size7x7, PixelTypes.Rgba32)] + [WithFile(Bpp4Size8x8, PixelTypes.Rgba32)] + [WithFile(Bpp4Size9x9, PixelTypes.Rgba32)] + [WithFile(Bpp4TranspNotSquare, PixelTypes.Rgba32)] + [WithFile(Bpp4TranspPartial, PixelTypes.Rgba32)] + [WithFile(Bpp8Size15x15, PixelTypes.Rgba32)] + [WithFile(Bpp8Size16x16, PixelTypes.Rgba32)] + [WithFile(Bpp8Size17x17, PixelTypes.Rgba32)] + [WithFile(Bpp8Size1x1, PixelTypes.Rgba32)] + [WithFile(Bpp8Size256x256, PixelTypes.Rgba32)] + [WithFile(Bpp8Size2x2, PixelTypes.Rgba32)] + [WithFile(Bpp8Size31x31, PixelTypes.Rgba32)] + [WithFile(Bpp8Size32x32, PixelTypes.Rgba32)] + [WithFile(Bpp8Size33x33, PixelTypes.Rgba32)] + [WithFile(Bpp8Size3x3, PixelTypes.Rgba32)] + [WithFile(Bpp8Size4x4, PixelTypes.Rgba32)] + [WithFile(Bpp8Size5x5, PixelTypes.Rgba32)] + [WithFile(Bpp8Size6x6, PixelTypes.Rgba32)] + [WithFile(Bpp8Size7x7, PixelTypes.Rgba32)] + [WithFile(Bpp8Size8x8, PixelTypes.Rgba32)] + [WithFile(Bpp8Size9x9, PixelTypes.Rgba32)] + [WithFile(Bpp8TranspNotSquare, PixelTypes.Rgba32)] + [WithFile(Bpp8TranspPartial, PixelTypes.Rgba32)] + [WithFile(InvalidAll, PixelTypes.Rgba32)] [WithFile(IcoFake, PixelTypes.Rgba32)] - [WithFile(Invalid_all, PixelTypes.Rgba32)] - [WithFile(Invalid_bpp, PixelTypes.Rgba32)] - [WithFile(Invalid_compression, PixelTypes.Rgba32)] - [WithFile(Invalid_png, PixelTypes.Rgba32)] - [WithFile(Invalid_RLE4, PixelTypes.Rgba32)] - [WithFile(Invalid_RLE8, PixelTypes.Rgba32)] - [WithFile(Mixed_bmp_png_a, PixelTypes.Rgba32)] - [WithFile(Mixed_bmp_png_b, PixelTypes.Rgba32)] - [WithFile(Mixed_bmp_png_c, PixelTypes.Rgba32)] - [WithFile(Multi_size_a, PixelTypes.Rgba32)] - [WithFile(Multi_size_b, PixelTypes.Rgba32)] - [WithFile(Multi_size_c, PixelTypes.Rgba32)] - [WithFile(Multi_size_d, PixelTypes.Rgba32)] - [WithFile(Multi_size_e, PixelTypes.Rgba32)] - [WithFile(Multi_size_f, PixelTypes.Rgba32)] - [WithFile(Multi_size_multi_bits_a, PixelTypes.Rgba32)] - [WithFile(Multi_size_multi_bits_b, PixelTypes.Rgba32)] - [WithFile(Multi_size_multi_bits_c, PixelTypes.Rgba32)] - [WithFile(Multi_size_multi_bits_d, PixelTypes.Rgba32)] + [WithFile(InvalidBpp, PixelTypes.Rgba32)] + [WithFile(InvalidCompression, PixelTypes.Rgba32)] + [WithFile(InvalidPng, PixelTypes.Rgba32)] + [WithFile(InvalidRLE4, PixelTypes.Rgba32)] + [WithFile(InvalidRLE8, PixelTypes.Rgba32)] + [WithFile(MixedBmpPngA, PixelTypes.Rgba32)] + [WithFile(MixedBmpPngB, PixelTypes.Rgba32)] + [WithFile(MixedBmpPngC, PixelTypes.Rgba32)] + [WithFile(MultiSizeA, PixelTypes.Rgba32)] + [WithFile(MultiSizeB, PixelTypes.Rgba32)] + [WithFile(MultiSizeC, PixelTypes.Rgba32)] + [WithFile(MultiSizeD, PixelTypes.Rgba32)] + [WithFile(MultiSizeE, PixelTypes.Rgba32)] + [WithFile(MultiSizeF, PixelTypes.Rgba32)] + [WithFile(MultiSizeMultiBitsA, PixelTypes.Rgba32)] + [WithFile(MultiSizeMultiBitsB, PixelTypes.Rgba32)] + [WithFile(MultiSizeMultiBitsC, PixelTypes.Rgba32)] + [WithFile(MultiSizeMultiBitsD, PixelTypes.Rgba32)] public void IcoDecoder_Decode2(TestImageProvider provider) { Debug.Assert(false); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 6db11c78f..fcb32eae6 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1126,118 +1126,118 @@ public static class TestImages public static class Ico { public const string Flutter = "Icon/flutter.ico"; - public const string Bpp1_size_15x15 = "Icon/1bpp_size_15x15.ico"; - public const string Bpp1_size_16x16 = "Icon/1bpp_size_16x16.ico"; - public const string Bpp1_size_17x17 = "Icon/1bpp_size_17x17.ico"; - public const string Bpp1_size_1x1 = "Icon/1bpp_size_1x1.ico"; - public const string Bpp1_size_256x256 = "Icon/1bpp_size_256x256.ico"; - public const string Bpp1_size_2x2 = "Icon/1bpp_size_2x2.ico"; - public const string Bpp1_size_31x31 = "Icon/1bpp_size_31x31.ico"; - public const string Bpp1_size_32x32 = "Icon/1bpp_size_32x32.ico"; - public const string Bpp1_size_33x33 = "Icon/1bpp_size_33x33.ico"; - public const string Bpp1_size_3x3 = "Icon/1bpp_size_3x3.ico"; - public const string Bpp1_size_4x4 = "Icon/1bpp_size_4x4.ico"; - public const string Bpp1_size_5x5 = "Icon/1bpp_size_5x5.ico"; - public const string Bpp1_size_6x6 = "Icon/1bpp_size_6x6.ico"; - public const string Bpp1_size_7x7 = "Icon/1bpp_size_7x7.ico"; - public const string Bpp1_size_8x8 = "Icon/1bpp_size_8x8.ico"; - public const string Bpp1_size_9x9 = "Icon/1bpp_size_9x9.ico"; - public const string Bpp1_transp_not_square = "Icon/1bpp_transp_not_square.ico"; - public const string Bpp1_transp_partial = "Icon/1bpp_transp_partial.ico"; - public const string Bpp24_size_15x15 = "Icon/24bpp_size_15x15.ico"; - public const string Bpp24_size_16x16 = "Icon/24bpp_size_16x16.ico"; - public const string Bpp24_size_17x17 = "Icon/24bpp_size_17x17.ico"; - public const string Bpp24_size_1x1 = "Icon/24bpp_size_1x1.ico"; - public const string Bpp24_size_256x256 = "Icon/24bpp_size_256x256.ico"; - public const string Bpp24_size_2x2 = "Icon/24bpp_size_2x2.ico"; - public const string Bpp24_size_31x31 = "Icon/24bpp_size_31x31.ico"; - public const string Bpp24_size_32x32 = "Icon/24bpp_size_32x32.ico"; - public const string Bpp24_size_33x33 = "Icon/24bpp_size_33x33.ico"; - public const string Bpp24_size_3x3 = "Icon/24bpp_size_3x3.ico"; - public const string Bpp24_size_4x4 = "Icon/24bpp_size_4x4.ico"; - public const string Bpp24_size_5x5 = "Icon/24bpp_size_5x5.ico"; - public const string Bpp24_size_6x6 = "Icon/24bpp_size_6x6.ico"; - public const string Bpp24_size_7x7 = "Icon/24bpp_size_7x7.ico"; - public const string Bpp24_size_8x8 = "Icon/24bpp_size_8x8.ico"; - public const string Bpp24_size_9x9 = "Icon/24bpp_size_9x9.ico"; - public const string Bpp24_transp_not_square = "Icon/24bpp_transp_not_square.ico"; - public const string Bpp24_transp_partial = "Icon/24bpp_transp_partial.ico"; - public const string Bpp24_transp = "Icon/24bpp_transp.ico"; - public const string Bpp32_size_15x15 = "Icon/32bpp_size_15x15.ico"; - public const string Bpp32_size_16x16 = "Icon/32bpp_size_16x16.ico"; - public const string Bpp32_size_17x17 = "Icon/32bpp_size_17x17.ico"; - public const string Bpp32_size_1x1 = "Icon/32bpp_size_1x1.ico"; - public const string Bpp32_size_256x256 = "Icon/32bpp_size_256x256.ico"; - public const string Bpp32_size_2x2 = "Icon/32bpp_size_2x2.ico"; - public const string Bpp32_size_31x31 = "Icon/32bpp_size_31x31.ico"; - public const string Bpp32_size_32x32 = "Icon/32bpp_size_32x32.ico"; - public const string Bpp32_size_33x33 = "Icon/32bpp_size_33x33.ico"; - public const string Bpp32_size_3x3 = "Icon/32bpp_size_3x3.ico"; - public const string Bpp32_size_4x4 = "Icon/32bpp_size_4x4.ico"; - public const string Bpp32_size_5x5 = "Icon/32bpp_size_5x5.ico"; - public const string Bpp32_size_6x6 = "Icon/32bpp_size_6x6.ico"; - public const string Bpp32_size_7x7 = "Icon/32bpp_size_7x7.ico"; - public const string Bpp32_size_8x8 = "Icon/32bpp_size_8x8.ico"; - public const string Bpp32_size_9x9 = "Icon/32bpp_size_9x9.ico"; - public const string Bpp32_transp_not_square = "Icon/32bpp_transp_not_square.ico"; - public const string Bpp32_transp_partial = "Icon/32bpp_transp_partial.ico"; - public const string Bpp32_transp = "Icon/32bpp_transp.ico"; - public const string Bpp4_size_15x15 = "Icon/4bpp_size_15x15.ico"; - public const string Bpp4_size_16x16 = "Icon/4bpp_size_16x16.ico"; - public const string Bpp4_size_17x17 = "Icon/4bpp_size_17x17.ico"; - public const string Bpp4_size_1x1 = "Icon/4bpp_size_1x1.ico"; - public const string Bpp4_size_256x256 = "Icon/4bpp_size_256x256.ico"; - public const string Bpp4_size_2x2 = "Icon/4bpp_size_2x2.ico"; - public const string Bpp4_size_31x31 = "Icon/4bpp_size_31x31.ico"; - public const string Bpp4_size_32x32 = "Icon/4bpp_size_32x32.ico"; - public const string Bpp4_size_33x33 = "Icon/4bpp_size_33x33.ico"; - public const string Bpp4_size_3x3 = "Icon/4bpp_size_3x3.ico"; - public const string Bpp4_size_4x4 = "Icon/4bpp_size_4x4.ico"; - public const string Bpp4_size_5x5 = "Icon/4bpp_size_5x5.ico"; - public const string Bpp4_size_6x6 = "Icon/4bpp_size_6x6.ico"; - public const string Bpp4_size_7x7 = "Icon/4bpp_size_7x7.ico"; - public const string Bpp4_size_8x8 = "Icon/4bpp_size_8x8.ico"; - public const string Bpp4_size_9x9 = "Icon/4bpp_size_9x9.ico"; - public const string Bpp4_transp_not_square = "Icon/4bpp_transp_not_square.ico"; - public const string Bpp4_transp_partial = "Icon/4bpp_transp_partial.ico"; - public const string Bpp8_size_15x15 = "Icon/8bpp_size_15x15.ico"; - public const string Bpp8_size_16x16 = "Icon/8bpp_size_16x16.ico"; - public const string Bpp8_size_17x17 = "Icon/8bpp_size_17x17.ico"; - public const string Bpp8_size_1x1 = "Icon/8bpp_size_1x1.ico"; - public const string Bpp8_size_256x256 = "Icon/8bpp_size_256x256.ico"; - public const string Bpp8_size_2x2 = "Icon/8bpp_size_2x2.ico"; - public const string Bpp8_size_31x31 = "Icon/8bpp_size_31x31.ico"; - public const string Bpp8_size_32x32 = "Icon/8bpp_size_32x32.ico"; - public const string Bpp8_size_33x33 = "Icon/8bpp_size_33x33.ico"; - public const string Bpp8_size_3x3 = "Icon/8bpp_size_3x3.ico"; - public const string Bpp8_size_4x4 = "Icon/8bpp_size_4x4.ico"; - public const string Bpp8_size_5x5 = "Icon/8bpp_size_5x5.ico"; - public const string Bpp8_size_6x6 = "Icon/8bpp_size_6x6.ico"; - public const string Bpp8_size_7x7 = "Icon/8bpp_size_7x7.ico"; - public const string Bpp8_size_8x8 = "Icon/8bpp_size_8x8.ico"; - public const string Bpp8_size_9x9 = "Icon/8bpp_size_9x9.ico"; - public const string Bpp8_transp_not_square = "Icon/8bpp_transp_not_square.ico"; - public const string Bpp8_transp_partial = "Icon/8bpp_transp_partial.ico"; - public const string Invalid_all = "Icon/invalid_all.ico"; + public const string Bpp1Size15x15 = "Icon/1bpp_size_15x15.ico"; + public const string Bpp1Size16x16 = "Icon/1bpp_size_16x16.ico"; + public const string Bpp1Size17x17 = "Icon/1bpp_size_17x17.ico"; + public const string Bpp1Size1x1 = "Icon/1bpp_size_1x1.ico"; + public const string Bpp1Size256x256 = "Icon/1bpp_size_256x256.ico"; + public const string Bpp1Size2x2 = "Icon/1bpp_size_2x2.ico"; + public const string Bpp1Size31x31 = "Icon/1bpp_size_31x31.ico"; + public const string Bpp1Size32x32 = "Icon/1bpp_size_32x32.ico"; + public const string Bpp1Size33x33 = "Icon/1bpp_size_33x33.ico"; + public const string Bpp1Size3x3 = "Icon/1bpp_size_3x3.ico"; + public const string Bpp1Size4x4 = "Icon/1bpp_size_4x4.ico"; + public const string Bpp1Size5x5 = "Icon/1bpp_size_5x5.ico"; + public const string Bpp1Size6x6 = "Icon/1bpp_size_6x6.ico"; + public const string Bpp1Size7x7 = "Icon/1bpp_size_7x7.ico"; + public const string Bpp1Size8x8 = "Icon/1bpp_size_8x8.ico"; + public const string Bpp1Size9x9 = "Icon/1bpp_size_9x9.ico"; + public const string Bpp1TranspNotSquare = "Icon/1bpp_transp_not_square.ico"; + public const string Bpp1TranspPartial = "Icon/1bpp_transp_partial.ico"; + public const string Bpp24Size15x15 = "Icon/24bpp_size_15x15.ico"; + public const string Bpp24Size16x16 = "Icon/24bpp_size_16x16.ico"; + public const string Bpp24Size17x17 = "Icon/24bpp_size_17x17.ico"; + public const string Bpp24Size1x1 = "Icon/24bpp_size_1x1.ico"; + public const string Bpp24Size256x256 = "Icon/24bpp_size_256x256.ico"; + public const string Bpp24Size2x2 = "Icon/24bpp_size_2x2.ico"; + public const string Bpp24Size31x31 = "Icon/24bpp_size_31x31.ico"; + public const string Bpp24Size32x32 = "Icon/24bpp_size_32x32.ico"; + public const string Bpp24Size33x33 = "Icon/24bpp_size_33x33.ico"; + public const string Bpp24Size3x3 = "Icon/24bpp_size_3x3.ico"; + public const string Bpp24Size4x4 = "Icon/24bpp_size_4x4.ico"; + public const string Bpp24Size5x5 = "Icon/24bpp_size_5x5.ico"; + public const string Bpp24Size6x6 = "Icon/24bpp_size_6x6.ico"; + public const string Bpp24Size7x7 = "Icon/24bpp_size_7x7.ico"; + public const string Bpp24Size8x8 = "Icon/24bpp_size_8x8.ico"; + public const string Bpp24Size9x9 = "Icon/24bpp_size_9x9.ico"; + public const string Bpp24TranspNotSquare = "Icon/24bpp_transp_not_square.ico"; + public const string Bpp24TranspPartial = "Icon/24bpp_transp_partial.ico"; + public const string Bpp24Transp = "Icon/24bpp_transp.ico"; + public const string Bpp32Size15x15 = "Icon/32bpp_size_15x15.ico"; + public const string Bpp32Size16x16 = "Icon/32bpp_size_16x16.ico"; + public const string Bpp32Size17x17 = "Icon/32bpp_size_17x17.ico"; + public const string Bpp32Size1x1 = "Icon/32bpp_size_1x1.ico"; + public const string Bpp32Size256x256 = "Icon/32bpp_size_256x256.ico"; + public const string Bpp32Size2x2 = "Icon/32bpp_size_2x2.ico"; + public const string Bpp32Size31x31 = "Icon/32bpp_size_31x31.ico"; + public const string Bpp32Size32x32 = "Icon/32bpp_size_32x32.ico"; + public const string Bpp32Size33x33 = "Icon/32bpp_size_33x33.ico"; + public const string Bpp32Size3x3 = "Icon/32bpp_size_3x3.ico"; + public const string Bpp32Size4x4 = "Icon/32bpp_size_4x4.ico"; + public const string Bpp32Size5x5 = "Icon/32bpp_size_5x5.ico"; + public const string Bpp32Size6x6 = "Icon/32bpp_size_6x6.ico"; + public const string Bpp32Size7x7 = "Icon/32bpp_size_7x7.ico"; + public const string Bpp32Size8x8 = "Icon/32bpp_size_8x8.ico"; + public const string Bpp32Size9x9 = "Icon/32bpp_size_9x9.ico"; + public const string Bpp32TranspNotSquare = "Icon/32bpp_transp_not_square.ico"; + public const string Bpp32TranspPartial = "Icon/32bpp_transp_partial.ico"; + public const string Bpp32Transp = "Icon/32bpp_transp.ico"; + public const string Bpp4Size15x15 = "Icon/4bpp_size_15x15.ico"; + public const string Bpp4Size16x16 = "Icon/4bpp_size_16x16.ico"; + public const string Bpp4Size17x17 = "Icon/4bpp_size_17x17.ico"; + public const string Bpp4Size1x1 = "Icon/4bpp_size_1x1.ico"; + public const string Bpp4Size256x256 = "Icon/4bpp_size_256x256.ico"; + public const string Bpp4Size2x2 = "Icon/4bpp_size_2x2.ico"; + public const string Bpp4Size31x31 = "Icon/4bpp_size_31x31.ico"; + public const string Bpp4Size32x32 = "Icon/4bpp_size_32x32.ico"; + public const string Bpp4Size33x33 = "Icon/4bpp_size_33x33.ico"; + public const string Bpp4Size3x3 = "Icon/4bpp_size_3x3.ico"; + public const string Bpp4Size4x4 = "Icon/4bpp_size_4x4.ico"; + public const string Bpp4Size5x5 = "Icon/4bpp_size_5x5.ico"; + public const string Bpp4Size6x6 = "Icon/4bpp_size_6x6.ico"; + public const string Bpp4Size7x7 = "Icon/4bpp_size_7x7.ico"; + public const string Bpp4Size8x8 = "Icon/4bpp_size_8x8.ico"; + public const string Bpp4Size9x9 = "Icon/4bpp_size_9x9.ico"; + public const string Bpp4TranspNotSquare = "Icon/4bpp_transp_not_square.ico"; + public const string Bpp4TranspPartial = "Icon/4bpp_transp_partial.ico"; + public const string Bpp8Size15x15 = "Icon/8bpp_size_15x15.ico"; + public const string Bpp8Size16x16 = "Icon/8bpp_size_16x16.ico"; + public const string Bpp8Size17x17 = "Icon/8bpp_size_17x17.ico"; + public const string Bpp8Size1x1 = "Icon/8bpp_size_1x1.ico"; + public const string Bpp8Size256x256 = "Icon/8bpp_size_256x256.ico"; + public const string Bpp8Size2x2 = "Icon/8bpp_size_2x2.ico"; + public const string Bpp8Size31x31 = "Icon/8bpp_size_31x31.ico"; + public const string Bpp8Size32x32 = "Icon/8bpp_size_32x32.ico"; + public const string Bpp8Size33x33 = "Icon/8bpp_size_33x33.ico"; + public const string Bpp8Size3x3 = "Icon/8bpp_size_3x3.ico"; + public const string Bpp8Size4x4 = "Icon/8bpp_size_4x4.ico"; + public const string Bpp8Size5x5 = "Icon/8bpp_size_5x5.ico"; + public const string Bpp8Size6x6 = "Icon/8bpp_size_6x6.ico"; + public const string Bpp8Size7x7 = "Icon/8bpp_size_7x7.ico"; + public const string Bpp8Size8x8 = "Icon/8bpp_size_8x8.ico"; + public const string Bpp8Size9x9 = "Icon/8bpp_size_9x9.ico"; + public const string Bpp8TranspNotSquare = "Icon/8bpp_transp_not_square.ico"; + public const string Bpp8TranspPartial = "Icon/8bpp_transp_partial.ico"; + public const string InvalidAll = "Icon/invalid_all.ico"; public const string IcoFake = "Icon/ico_fake.cur"; - public const string Invalid_bpp = "Icon/invalid_bpp.ico"; - public const string Invalid_compression = "Icon/invalid_compression.ico"; - public const string Invalid_png = "Icon/invalid_png.ico"; - public const string Invalid_RLE4 = "Icon/invalid_RLE4.ico"; - public const string Invalid_RLE8 = "Icon/invalid_RLE8.ico"; - public const string Mixed_bmp_png_a = "Icon/mixed_bmp_png_a.ico"; - public const string Mixed_bmp_png_b = "Icon/mixed_bmp_png_b.ico"; - public const string Mixed_bmp_png_c = "Icon/mixed_bmp_png_c.ico"; - public const string Multi_size_a = "Icon/multi_size_a.ico"; - public const string Multi_size_b = "Icon/multi_size_b.ico"; - public const string Multi_size_c = "Icon/multi_size_c.ico"; - public const string Multi_size_d = "Icon/multi_size_d.ico"; - public const string Multi_size_e = "Icon/multi_size_e.ico"; - public const string Multi_size_f = "Icon/multi_size_f.ico"; - public const string Multi_size_multi_bits_a = "Icon/multi_size_multi_bits_a.ico"; - public const string Multi_size_multi_bits_b = "Icon/multi_size_multi_bits_b.ico"; - public const string Multi_size_multi_bits_c = "Icon/multi_size_multi_bits_c.ico"; - public const string Multi_size_multi_bits_d = "Icon/multi_size_multi_bits_d.ico"; + public const string InvalidBpp = "Icon/invalid_bpp.ico"; + public const string InvalidCompression = "Icon/invalid_compression.ico"; + public const string InvalidPng = "Icon/invalid_png.ico"; + public const string InvalidRLE4 = "Icon/invalid_RLE4.ico"; + public const string InvalidRLE8 = "Icon/invalid_RLE8.ico"; + public const string MixedBmpPngA = "Icon/mixed_bmp_png_a.ico"; + public const string MixedBmpPngB = "Icon/mixed_bmp_png_b.ico"; + public const string MixedBmpPngC = "Icon/mixed_bmp_png_c.ico"; + public const string MultiSizeA = "Icon/multi_size_a.ico"; + public const string MultiSizeB = "Icon/multi_size_b.ico"; + public const string MultiSizeC = "Icon/multi_size_c.ico"; + public const string MultiSizeD = "Icon/multi_size_d.ico"; + public const string MultiSizeE = "Icon/multi_size_e.ico"; + public const string MultiSizeF = "Icon/multi_size_f.ico"; + public const string MultiSizeMultiBitsA = "Icon/multi_size_multi_bits_a.ico"; + public const string MultiSizeMultiBitsB = "Icon/multi_size_multi_bits_b.ico"; + public const string MultiSizeMultiBitsC = "Icon/multi_size_multi_bits_c.ico"; + public const string MultiSizeMultiBitsD = "Icon/multi_size_multi_bits_d.ico"; } public static class Cur From 237d129afcf96b1e9876f079cc3d99a199450658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Tue, 18 Jun 2024 21:45:45 +0800 Subject: [PATCH 71/79] Update Tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/Cur/CurDecoderTests.cs | 12 +++ .../Formats/Icon/Cur/CurEncoderTests.cs | 9 -- .../Formats/Icon/Ico/IcoDecoderTests.cs | 101 +++++++++++++++++- tests/ImageSharp.Tests/TestImages.cs | 2 +- 4 files changed, 110 insertions(+), 14 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs index d1587d35b..e6efda4b6 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs @@ -21,4 +21,16 @@ public class CurDecoderTests // TODO: Assert metadata, frame count, etc } + + [Theory] + [WithFile(CurFake, PixelTypes.Rgba32)] + [WithFile(CurReal, PixelTypes.Rgba32)] + public void CurDecoder_Decode2(TestImageProvider provider) + { + using Image image = provider.GetImage(CurDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs index 58213397c..9908786f1 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Diagnostics; using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Cur; @@ -30,12 +29,4 @@ public class CurEncoderTests memStream.Seek(0, SeekOrigin.Begin); CurDecoder.Instance.Decode(new(), memStream); } - - [Theory] - [WithFile(CurFake, PixelTypes.Rgba32)] - [WithFile(CurReal, PixelTypes.Rgba32)] - public void CurDecoder_Decode2(TestImageProvider provider) - { - Debug.Assert(false); - } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index 8ea7cf6d4..981a1031d 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System.Diagnostics; using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Ico; @@ -42,6 +41,16 @@ public class IcoDecoderTests [WithFile(Bpp1Size9x9, PixelTypes.Rgba32)] [WithFile(Bpp1TranspNotSquare, PixelTypes.Rgba32)] [WithFile(Bpp1TranspPartial, PixelTypes.Rgba32)] + public void Bpp1Test(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(Bpp24Size15x15, PixelTypes.Rgba32)] [WithFile(Bpp24Size16x16, PixelTypes.Rgba32)] [WithFile(Bpp24Size17x17, PixelTypes.Rgba32)] @@ -61,6 +70,16 @@ public class IcoDecoderTests [WithFile(Bpp24TranspNotSquare, PixelTypes.Rgba32)] [WithFile(Bpp24TranspPartial, PixelTypes.Rgba32)] [WithFile(Bpp24Transp, PixelTypes.Rgba32)] + public void Bpp24Test(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(Bpp32Size15x15, PixelTypes.Rgba32)] [WithFile(Bpp32Size16x16, PixelTypes.Rgba32)] [WithFile(Bpp32Size17x17, PixelTypes.Rgba32)] @@ -80,6 +99,16 @@ public class IcoDecoderTests [WithFile(Bpp32TranspNotSquare, PixelTypes.Rgba32)] [WithFile(Bpp32TranspPartial, PixelTypes.Rgba32)] [WithFile(Bpp32Transp, PixelTypes.Rgba32)] + public void Bpp32Test(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(Bpp4Size15x15, PixelTypes.Rgba32)] [WithFile(Bpp4Size16x16, PixelTypes.Rgba32)] [WithFile(Bpp4Size17x17, PixelTypes.Rgba32)] @@ -98,6 +127,16 @@ public class IcoDecoderTests [WithFile(Bpp4Size9x9, PixelTypes.Rgba32)] [WithFile(Bpp4TranspNotSquare, PixelTypes.Rgba32)] [WithFile(Bpp4TranspPartial, PixelTypes.Rgba32)] + public void Bpp4Test(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(Bpp8Size15x15, PixelTypes.Rgba32)] [WithFile(Bpp8Size16x16, PixelTypes.Rgba32)] [WithFile(Bpp8Size17x17, PixelTypes.Rgba32)] @@ -116,28 +155,82 @@ public class IcoDecoderTests [WithFile(Bpp8Size9x9, PixelTypes.Rgba32)] [WithFile(Bpp8TranspNotSquare, PixelTypes.Rgba32)] [WithFile(Bpp8TranspPartial, PixelTypes.Rgba32)] + public void Bpp8Test(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(InvalidAll, PixelTypes.Rgba32)] - [WithFile(IcoFake, PixelTypes.Rgba32)] [WithFile(InvalidBpp, PixelTypes.Rgba32)] [WithFile(InvalidCompression, PixelTypes.Rgba32)] [WithFile(InvalidPng, PixelTypes.Rgba32)] [WithFile(InvalidRLE4, PixelTypes.Rgba32)] [WithFile(InvalidRLE8, PixelTypes.Rgba32)] + public void InvalidTest(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(MixedBmpPngA, PixelTypes.Rgba32)] [WithFile(MixedBmpPngB, PixelTypes.Rgba32)] [WithFile(MixedBmpPngC, PixelTypes.Rgba32)] + public void MixedBmpPngTest(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(MultiSizeA, PixelTypes.Rgba32)] [WithFile(MultiSizeB, PixelTypes.Rgba32)] [WithFile(MultiSizeC, PixelTypes.Rgba32)] [WithFile(MultiSizeD, PixelTypes.Rgba32)] [WithFile(MultiSizeE, PixelTypes.Rgba32)] [WithFile(MultiSizeF, PixelTypes.Rgba32)] + public void MultiSizeTest(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] [WithFile(MultiSizeMultiBitsA, PixelTypes.Rgba32)] [WithFile(MultiSizeMultiBitsB, PixelTypes.Rgba32)] [WithFile(MultiSizeMultiBitsC, PixelTypes.Rgba32)] [WithFile(MultiSizeMultiBitsD, PixelTypes.Rgba32)] - public void IcoDecoder_Decode2(TestImageProvider provider) + public void MultiSizeMultiBitsTest(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + + [Theory] + [WithFile(IcoFake, PixelTypes.Rgba32)] + public void IcoFakeTest(TestImageProvider provider) { - Debug.Assert(false); + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index fcb32eae6..8937799e1 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1219,7 +1219,6 @@ public static class TestImages public const string Bpp8TranspNotSquare = "Icon/8bpp_transp_not_square.ico"; public const string Bpp8TranspPartial = "Icon/8bpp_transp_partial.ico"; public const string InvalidAll = "Icon/invalid_all.ico"; - public const string IcoFake = "Icon/ico_fake.cur"; public const string InvalidBpp = "Icon/invalid_bpp.ico"; public const string InvalidCompression = "Icon/invalid_compression.ico"; public const string InvalidPng = "Icon/invalid_png.ico"; @@ -1238,6 +1237,7 @@ public static class TestImages public const string MultiSizeMultiBitsB = "Icon/multi_size_multi_bits_b.ico"; public const string MultiSizeMultiBitsC = "Icon/multi_size_multi_bits_c.ico"; public const string MultiSizeMultiBitsD = "Icon/multi_size_multi_bits_d.ico"; + public const string IcoFake = "Icon/ico_fake.cur"; } public static class Cur From 1f4c7e3360fc6397be6d3f64a719f3b84b86e56a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Tue, 18 Jun 2024 21:56:34 +0800 Subject: [PATCH 72/79] Assert Invalid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/Ico/IcoDecoderTests.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index 981a1031d..c964733c3 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -172,13 +172,14 @@ public class IcoDecoderTests [WithFile(InvalidRLE4, PixelTypes.Rgba32)] [WithFile(InvalidRLE8, PixelTypes.Rgba32)] public void InvalidTest(TestImageProvider provider) - { - using Image image = provider.GetImage(IcoDecoder.Instance); + => Assert.Throws(() => + { + using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSaveMultiFrame(provider, extension: "png"); - // TODO: Assert metadata, frame count, etc - } + // TODO: Assert metadata, frame count, etc + }); [Theory] [WithFile(MixedBmpPngA, PixelTypes.Rgba32)] From 9a7727b3148bd461726b3cfdeab5592b57a2d273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=B0=E9=98=9F=E7=9A=84=E5=81=B6=E5=83=8F-=E5=B2=9B?= =?UTF-8?q?=E9=A3=8E=E9=85=B1!?= Date: Tue, 18 Jun 2024 22:03:06 +0800 Subject: [PATCH 73/79] Pass the Test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 舰队的偶像-岛风酱! --- .../Formats/Icon/Ico/IcoDecoderTests.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index c964733c3..1c724ca35 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -168,7 +168,6 @@ public class IcoDecoderTests [WithFile(InvalidAll, PixelTypes.Rgba32)] [WithFile(InvalidBpp, PixelTypes.Rgba32)] [WithFile(InvalidCompression, PixelTypes.Rgba32)] - [WithFile(InvalidPng, PixelTypes.Rgba32)] [WithFile(InvalidRLE4, PixelTypes.Rgba32)] [WithFile(InvalidRLE8, PixelTypes.Rgba32)] public void InvalidTest(TestImageProvider provider) @@ -181,6 +180,17 @@ public class IcoDecoderTests // TODO: Assert metadata, frame count, etc }); + [Theory] + [WithFile(InvalidPng, PixelTypes.Rgba32)] + public void InvalidPngTest(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance); + + image.DebugSaveMultiFrame(provider, extension: "png"); + + // TODO: Assert metadata, frame count, etc + } + [Theory] [WithFile(MixedBmpPngA, PixelTypes.Rgba32)] [WithFile(MixedBmpPngB, PixelTypes.Rgba32)] From b31bd2337c57c39a62d4be09c7db68d264b0eea7 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 19 Jun 2024 16:24:31 +1000 Subject: [PATCH 74/79] Complete tests and fix issues --- src/ImageSharp/Formats/Cur/CurDecoderCore.cs | 8 +- src/ImageSharp/Formats/Cur/CurEncoder.cs | 2 +- src/ImageSharp/Formats/Cur/CurEncoderCore.cs | 4 +- .../Formats/Cur/CurFrameMetadata.cs | 25 ++-- src/ImageSharp/Formats/Ico/IcoDecoderCore.cs | 8 +- src/ImageSharp/Formats/Ico/IcoEncoder.cs | 4 +- src/ImageSharp/Formats/Ico/IcoEncoderCore.cs | 4 +- .../Formats/Ico/IcoFrameMetadata.cs | 26 +++- .../Formats/Icon/IconDecoderCore.cs | 81 ++++++++-- .../Formats/Icon/IconEncoderCore.cs | 72 ++++++--- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 31 +++- .../Quantization/PaletteQuantizer.cs | 7 +- .../Formats/Icon/Cur/CurDecoderTests.cs | 19 ++- .../Formats/Icon/Cur/CurEncoderTests.cs | 26 ++-- .../Formats/Icon/Ico/IcoDecoderTests.cs | 139 ++++++++++++++---- .../Formats/Icon/Ico/IcoEncoderTests.cs | 26 ++-- 16 files changed, 363 insertions(+), 119 deletions(-) diff --git a/src/ImageSharp/Formats/Cur/CurDecoderCore.cs b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs index 18ab8c75a..3018ec6bf 100644 --- a/src/ImageSharp/Formats/Cur/CurDecoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurDecoderCore.cs @@ -14,11 +14,17 @@ internal sealed class CurDecoderCore : IconDecoderCore { } - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel) + protected override void SetFrameMetadata( + ImageFrameMetadata metadata, + in IconDirEntry entry, + IconFrameCompression compression, + BmpBitsPerPixel bitsPerPixel, + ReadOnlyMemory? colorTable) { CurFrameMetadata curFrameMetadata = metadata.GetCurMetadata(); curFrameMetadata.FromIconDirEntry(entry); curFrameMetadata.Compression = compression; curFrameMetadata.BmpBitsPerPixel = bitsPerPixel; + curFrameMetadata.ColorTable = colorTable; } } diff --git a/src/ImageSharp/Formats/Cur/CurEncoder.cs b/src/ImageSharp/Formats/Cur/CurEncoder.cs index d237fe7d0..e19a73990 100644 --- a/src/ImageSharp/Formats/Cur/CurEncoder.cs +++ b/src/ImageSharp/Formats/Cur/CurEncoder.cs @@ -11,7 +11,7 @@ public sealed class CurEncoder : QuantizingImageEncoder /// protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { - CurEncoderCore encoderCore = new(); + CurEncoderCore encoderCore = new(this); encoderCore.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs index a6922d431..6435587e2 100644 --- a/src/ImageSharp/Formats/Cur/CurEncoderCore.cs +++ b/src/ImageSharp/Formats/Cur/CurEncoderCore.cs @@ -7,8 +7,8 @@ namespace SixLabors.ImageSharp.Formats.Cur; internal sealed class CurEncoderCore : IconEncoderCore { - public CurEncoderCore() - : base(IconFileType.CUR) + public CurEncoderCore(QuantizingImageEncoder encoder) + : base(encoder, IconFileType.CUR) { } } diff --git a/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs b/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs index fc5cc5b2c..014944ba6 100644 --- a/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs +++ b/src/ImageSharp/Formats/Cur/CurFrameMetadata.cs @@ -3,6 +3,7 @@ using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Icon; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Cur; @@ -18,14 +19,14 @@ public class CurFrameMetadata : IDeepCloneable, IDeepCloneable { } - private CurFrameMetadata(CurFrameMetadata metadata) + private CurFrameMetadata(CurFrameMetadata other) { - this.Compression = metadata.Compression; - this.HotspotX = metadata.HotspotX; - this.HotspotY = metadata.HotspotY; - this.EncodingWidth = metadata.EncodingWidth; - this.EncodingHeight = metadata.EncodingHeight; - this.BmpBitsPerPixel = metadata.BmpBitsPerPixel; + this.Compression = other.Compression; + this.HotspotX = other.HotspotX; + this.HotspotY = other.HotspotY; + this.EncodingWidth = other.EncodingWidth; + this.EncodingHeight = other.EncodingHeight; + this.BmpBitsPerPixel = other.BmpBitsPerPixel; } /// @@ -45,13 +46,13 @@ public class CurFrameMetadata : IDeepCloneable, IDeepCloneable /// /// Gets or sets the encoding width.
- /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater. ///
public byte EncodingWidth { get; set; } /// /// Gets or sets the encoding height.
- /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater. ///
public byte EncodingHeight { get; set; } @@ -61,6 +62,12 @@ public class CurFrameMetadata : IDeepCloneable, IDeepCloneable ///
public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel32; + /// + /// Gets or sets the color table, if any. + /// The underlying pixel format is represented by . + /// + public ReadOnlyMemory? ColorTable { get; set; } + /// public CurFrameMetadata DeepClone() => new(this); diff --git a/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs index e8629e35b..f4990c66a 100644 --- a/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoDecoderCore.cs @@ -14,11 +14,17 @@ internal sealed class IcoDecoderCore : IconDecoderCore { } - protected override void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel) + protected override void SetFrameMetadata( + ImageFrameMetadata metadata, + in IconDirEntry entry, + IconFrameCompression compression, + BmpBitsPerPixel bitsPerPixel, + ReadOnlyMemory? colorTable) { IcoFrameMetadata icoFrameMetadata = metadata.GetIcoMetadata(); icoFrameMetadata.FromIconDirEntry(entry); icoFrameMetadata.Compression = compression; icoFrameMetadata.BmpBitsPerPixel = bitsPerPixel; + icoFrameMetadata.ColorTable = colorTable; } } diff --git a/src/ImageSharp/Formats/Ico/IcoEncoder.cs b/src/ImageSharp/Formats/Ico/IcoEncoder.cs index 298f93dec..e72925156 100644 --- a/src/ImageSharp/Formats/Ico/IcoEncoder.cs +++ b/src/ImageSharp/Formats/Ico/IcoEncoder.cs @@ -6,12 +6,12 @@ namespace SixLabors.ImageSharp.Formats.Ico; /// /// Image encoder for writing an image to a stream as a Windows Icon. /// -public sealed class IcoEncoder : ImageEncoder +public sealed class IcoEncoder : QuantizingImageEncoder { /// protected override void Encode(Image image, Stream stream, CancellationToken cancellationToken) { - IcoEncoderCore encoderCore = new(); + IcoEncoderCore encoderCore = new(this); encoderCore.Encode(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs index ab3edfbd3..f3cacb1b9 100644 --- a/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs +++ b/src/ImageSharp/Formats/Ico/IcoEncoderCore.cs @@ -7,8 +7,8 @@ namespace SixLabors.ImageSharp.Formats.Ico; internal sealed class IcoEncoderCore : IconEncoderCore { - public IcoEncoderCore() - : base(IconFileType.ICO) + public IcoEncoderCore(QuantizingImageEncoder encoder) + : base(encoder, IconFileType.ICO) { } } diff --git a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs index 82e4ce3b2..ea27d13c8 100644 --- a/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs +++ b/src/ImageSharp/Formats/Ico/IcoFrameMetadata.cs @@ -3,6 +3,7 @@ using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Icon; +using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Ico; @@ -18,12 +19,17 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable { } - private IcoFrameMetadata(IcoFrameMetadata metadata) + private IcoFrameMetadata(IcoFrameMetadata other) { - this.Compression = metadata.Compression; - this.EncodingWidth = metadata.EncodingWidth; - this.EncodingHeight = metadata.EncodingHeight; - this.BmpBitsPerPixel = metadata.BmpBitsPerPixel; + this.Compression = other.Compression; + this.EncodingWidth = other.EncodingWidth; + this.EncodingHeight = other.EncodingHeight; + this.BmpBitsPerPixel = other.BmpBitsPerPixel; + + if (other.ColorTable?.Length > 0) + { + this.ColorTable = other.ColorTable.Value.ToArray(); + } } /// @@ -33,13 +39,13 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable /// /// Gets or sets the encoding width.
- /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater. ///
public byte EncodingWidth { get; set; } /// /// Gets or sets the encoding height.
- /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels. + /// Can be any number between 0 and 255. Value 0 means a frame height of 256 pixels or greater. ///
public byte EncodingHeight { get; set; } @@ -49,6 +55,12 @@ public class IcoFrameMetadata : IDeepCloneable, IDeepCloneable ///
public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel32; + /// + /// Gets or sets the color table, if any. + /// The underlying pixel format is represented by . + /// + public ReadOnlyMemory? ColorTable { get; set; } + /// public IcoFrameMetadata DeepClone() => new(this); diff --git a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs index a0849fa61..74fe7b9e5 100644 --- a/src/ImageSharp/Formats/Icon/IconDecoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconDecoderCore.cs @@ -31,10 +31,16 @@ internal abstract class IconDecoderCore : IImageDecoderInternals Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; - List<(Image Image, IconFrameCompression Compression, int Index)> decodedEntries = new(this.entries.Length); + List<(Image Image, IconFrameCompression Compression, int Index)> decodedEntries + = new((int)Math.Min(this.entries.Length, this.Options.MaxFrames)); for (int i = 0; i < this.entries.Length; i++) { + if (i == this.Options.MaxFrames) + { + break; + } + ref IconDirEntry entry = ref this.entries[i]; // If we hit the end of the stream we should break. @@ -69,6 +75,7 @@ internal abstract class IconDecoderCore : IImageDecoderInternals Image result = new(this.Options.Configuration, metadata, decodedEntries.Select(x => { BmpBitsPerPixel bitsPerPixel = BmpBitsPerPixel.Pixel32; + ReadOnlyMemory? colorTable = null; ImageFrame target = new(this.Options.Configuration, this.Dimensions); ImageFrame source = x.Image.Frames.RootFrameUnsafe; for (int y = 0; y < source.Height; y++) @@ -88,11 +95,22 @@ internal abstract class IconDecoderCore : IImageDecoderInternals } else { - bmpMetadata = x.Image.Metadata.GetBmpMetadata(); - bitsPerPixel = bmpMetadata.BitsPerPixel; + BmpMetadata meta = x.Image.Metadata.GetBmpMetadata(); + bitsPerPixel = meta.BitsPerPixel; + colorTable = meta.ColorTable; + + if (x.Index == 0) + { + bmpMetadata = meta; + } } - this.SetFrameMetadata(target.Metadata, this.entries[x.Index], x.Compression, bitsPerPixel); + this.SetFrameMetadata( + target.Metadata, + this.entries[x.Index], + x.Compression, + bitsPerPixel, + colorTable); x.Image.Dispose(); @@ -122,11 +140,14 @@ internal abstract class IconDecoderCore : IImageDecoderInternals Span flag = stackalloc byte[PngConstants.HeaderBytes.Length]; ImageMetadata metadata = new(); - ImageFrameMetadata[] frames = new ImageFrameMetadata[this.fileHeader.Count]; + BmpMetadata? bmpMetadata = null; + PngMetadata? pngMetadata = null; + ImageFrameMetadata[] frames = new ImageFrameMetadata[Math.Min(this.fileHeader.Count, this.Options.MaxFrames)]; int bpp = 0; for (int i = 0; i < frames.Length; i++) { BmpBitsPerPixel bitsPerPixel = BmpBitsPerPixel.Pixel32; + ReadOnlyMemory? colorTable = null; ref IconDirEntry entry = ref this.entries[i]; // If we hit the end of the stream we should break. @@ -149,25 +170,65 @@ internal abstract class IconDecoderCore : IImageDecoderInternals // Decode the frame into a temp image buffer. This is disposed after the frame is copied to the result. ImageInfo temp = this.GetDecoder(isPng).Identify(stream, cancellationToken); - frames[i] = new(); - if (!isPng) + ImageFrameMetadata frameMetadata = new(); + + if (isPng) + { + if (i == 0) + { + pngMetadata = temp.Metadata.GetPngMetadata(); + } + + frameMetadata.SetFormatMetadata(PngFormat.Instance, temp.FrameMetadataCollection[0].GetPngMetadata()); + } + else { - bitsPerPixel = temp.Metadata.GetBmpMetadata().BitsPerPixel; + BmpMetadata meta = temp.Metadata.GetBmpMetadata(); + bitsPerPixel = meta.BitsPerPixel; + colorTable = meta.ColorTable; + + if (i == 0) + { + bmpMetadata = meta; + } } bpp = Math.Max(bpp, (int)bitsPerPixel); - this.SetFrameMetadata(frames[i], this.entries[i], isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, bitsPerPixel); + frames[i] = frameMetadata; + + this.SetFrameMetadata( + frames[i], + this.entries[i], + isPng ? IconFrameCompression.Png : IconFrameCompression.Bmp, + bitsPerPixel, + colorTable); // Since Windows Vista, the size of an image is determined from the BITMAPINFOHEADER structure or PNG image data // which technically allows storing icons with larger than 256 pixels, but such larger sizes are not recommended by Microsoft. this.Dimensions = new(Math.Max(this.Dimensions.Width, temp.Size.Width), Math.Max(this.Dimensions.Height, temp.Size.Height)); } + // Copy the format specific metadata to the image. + if (bmpMetadata != null) + { + metadata.SetFormatMetadata(BmpFormat.Instance, bmpMetadata); + } + + if (pngMetadata != null) + { + metadata.SetFormatMetadata(PngFormat.Instance, pngMetadata); + } + return new(new(bpp), this.Dimensions, metadata, frames); } - protected abstract void SetFrameMetadata(ImageFrameMetadata metadata, in IconDirEntry entry, IconFrameCompression compression, BmpBitsPerPixel bitsPerPixel); + protected abstract void SetFrameMetadata( + ImageFrameMetadata metadata, + in IconDirEntry entry, + IconFrameCompression compression, + BmpBitsPerPixel bitsPerPixel, + ReadOnlyMemory? colorTable); [MemberNotNull(nameof(entries))] protected void ReadHeader(Stream stream) diff --git a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs index eb07ab483..243339661 100644 --- a/src/ImageSharp/Formats/Icon/IconEncoderCore.cs +++ b/src/ImageSharp/Formats/Icon/IconEncoderCore.cs @@ -13,12 +13,16 @@ namespace SixLabors.ImageSharp.Formats.Icon; internal abstract class IconEncoderCore : IImageEncoderInternals { + private readonly QuantizingImageEncoder encoder; private readonly IconFileType iconFileType; private IconDir fileHeader; private EncodingFrameMetadata[]? entries; - protected IconEncoderCore(IconFileType iconFileType) - => this.iconFileType = iconFileType; + protected IconEncoderCore(QuantizingImageEncoder encoder, IconFileType iconFileType) + { + this.encoder = encoder; + this.iconFileType = iconFileType; + } public void Encode( Image image, @@ -71,14 +75,7 @@ internal abstract class IconEncoderCore : IImageEncoderInternals { IconFrameCompression.Bmp => new BmpEncoder() { - // We don't have access to the palette in the metadata so we need to quantize the image - // using a new one generated from the pixel data. - Quantizer = encodingMetadata.Entry.BitCount <= 8 - ? new WuQuantizer(new() - { - MaxColors = encodingMetadata.Entry.ColorCount - }) - : null, + Quantizer = this.GetQuantizer(encodingMetadata), ProcessedAlphaMask = true, UseDoubleHeight = true, SkipFileHeader = true, @@ -122,28 +119,65 @@ internal abstract class IconEncoderCore : IImageEncoderInternals image.Frames.Select(i => { IcoFrameMetadata metadata = i.Metadata.GetIcoMetadata(); - return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ToIconDirEntry()); + return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ColorTable, metadata.ToIconDirEntry()); }).ToArray(), IconFileType.CUR => image.Frames.Select(i => { CurFrameMetadata metadata = i.Metadata.GetCurMetadata(); - return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ToIconDirEntry()); + return new EncodingFrameMetadata(metadata.Compression, metadata.BmpBitsPerPixel, metadata.ColorTable, metadata.ToIconDirEntry()); }).ToArray(), _ => throw new NotSupportedException(), }; } - internal sealed class EncodingFrameMetadata( - IconFrameCompression compression, - BmpBitsPerPixel bmpBitsPerPixel, - IconDirEntry iconDirEntry) + private IQuantizer? GetQuantizer(EncodingFrameMetadata metadata) { - private IconDirEntry iconDirEntry = iconDirEntry; + if (metadata.Entry.BitCount > 8) + { + return null; + } + + if (this.encoder.Quantizer is not null) + { + return this.encoder.Quantizer; + } + + if (metadata.ColorTable is null) + { + return new WuQuantizer(new() + { + MaxColors = metadata.Entry.ColorCount + }); + } + + // Don't dither if we have a palette. We want to preserve as much information as possible. + return new PaletteQuantizer(metadata.ColorTable.Value, new() { Dither = null }); + } + + internal sealed class EncodingFrameMetadata + { + private IconDirEntry iconDirEntry; + + public EncodingFrameMetadata( + IconFrameCompression compression, + BmpBitsPerPixel bmpBitsPerPixel, + ReadOnlyMemory? colorTable, + IconDirEntry iconDirEntry) + { + this.Compression = compression; + this.BmpBitsPerPixel = compression == IconFrameCompression.Png + ? BmpBitsPerPixel.Pixel32 + : bmpBitsPerPixel; + this.ColorTable = colorTable; + this.iconDirEntry = iconDirEntry; + } + + public IconFrameCompression Compression { get; } - public IconFrameCompression Compression { get; set; } = compression; + public BmpBitsPerPixel BmpBitsPerPixel { get; } - public BmpBitsPerPixel BmpBitsPerPixel { get; set; } = bmpBitsPerPixel; + public ReadOnlyMemory? ColorTable { get; set; } public ref IconDirEntry Entry => ref this.iconDirEntry; } diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 36a0a8bcb..3e278be14 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -345,9 +345,10 @@ internal sealed class PngDecoderCore : IImageDecoderInternals { uint frameCount = 0; ImageMetadata metadata = new(); + List framesMetadata = []; PngMetadata pngMetadata = metadata.GetPngMetadata(); this.currentStream = stream; - FrameControl? lastFrameControl = null; + FrameControl? currentFrameControl = null; Span buffer = stackalloc byte[20]; this.currentStream.Skip(8); @@ -400,7 +401,8 @@ internal sealed class PngDecoderCore : IImageDecoderInternals break; } - lastFrameControl = this.ReadFrameControlChunk(chunk.Data.GetSpan()); + currentFrameControl = this.ReadFrameControlChunk(chunk.Data.GetSpan()); + break; case PngChunkType.FrameData: if (frameCount == this.maxFrames) @@ -413,22 +415,35 @@ internal sealed class PngDecoderCore : IImageDecoderInternals goto EOF; } - if (lastFrameControl is null) + if (currentFrameControl is null) { PngThrowHelper.ThrowMissingFrameControl(); } + InitializeFrameMetadata(framesMetadata, currentFrameControl.Value); + // Skip sequence number this.currentStream.Skip(4); this.SkipChunkDataAndCrc(chunk); break; case PngChunkType.Data: + // Spec says tRNS must be before IDAT so safe to exit. if (this.colorMetadataOnly) { goto EOF; } + pngMetadata.AnimateRootFrame = currentFrameControl != null; + currentFrameControl ??= new((uint)this.header.Width, (uint)this.header.Height); + if (framesMetadata.Count == 0) + { + InitializeFrameMetadata(framesMetadata, currentFrameControl.Value); + + // Both PLTE and tRNS chunks, if present, have been read at this point as per spec. + AssignColorPalette(this.palette, this.paletteAlpha, pngMetadata); + } + this.SkipChunkDataAndCrc(chunk); break; case PngChunkType.Palette: @@ -515,7 +530,7 @@ internal sealed class PngDecoderCore : IImageDecoderInternals // Both PLTE and tRNS chunks, if present, have been read at this point as per spec. AssignColorPalette(this.palette, this.paletteAlpha, pngMetadata); - return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), new(this.header.Width, this.header.Height), metadata); + return new ImageInfo(new PixelTypeInfo(this.CalculateBitsPerPixel()), new(this.header.Width, this.header.Height), metadata, framesMetadata); } finally { @@ -680,6 +695,14 @@ internal sealed class PngDecoderCore : IImageDecoderInternals this.scanline = this.configuration.MemoryAllocator.Allocate(this.bytesPerScanline, AllocationOptions.Clean); } + private static void InitializeFrameMetadata(List imageFrameMetadata, FrameControl currentFrameControl) + { + ImageFrameMetadata meta = new(); + PngFrameMetadata frameMetadata = meta.GetPngMetadata(); + frameMetadata.FromChunk(currentFrameControl); + imageFrameMetadata.Add(meta); + } + /// /// Calculates the correct number of bits per pixel for the given color type. /// diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs index acd179ffc..13a59a26d 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs @@ -62,9 +62,10 @@ public class PaletteQuantizer : IQuantizer { Guard.NotNull(options, nameof(options)); - // Always use the palette length over options since the palette cannot be reduced. - TPixel[] palette = new TPixel[this.colorPalette.Length]; - Color.ToPixel(this.colorPalette.Span, palette.AsSpan()); + // If the palette is larger than the max colors then we need to trim it down. + // treat the buffer as FILO. + TPixel[] palette = new TPixel[Math.Min(options.MaxColors, this.colorPalette.Length)]; + Color.ToPixel(this.colorPalette.Span[..palette.Length], palette.AsSpan()); return new PaletteQuantizer(configuration, options, palette, this.transparentIndex); } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs index e6efda4b6..4efd33648 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurDecoderTests.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Cur; +using SixLabors.ImageSharp.Formats.Icon; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Cur; @@ -17,9 +19,11 @@ public class CurDecoderTests { using Image image = provider.GetImage(CurDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); - - // TODO: Assert metadata, frame count, etc + CurFrameMetadata meta = image.Frames[0].Metadata.GetCurMetadata(); + Assert.Equal(image.Width, meta.EncodingWidth); + Assert.Equal(image.Height, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); } [Theory] @@ -28,9 +32,10 @@ public class CurDecoderTests public void CurDecoder_Decode2(TestImageProvider provider) { using Image image = provider.GetImage(CurDecoder.Instance); - - image.DebugSaveMultiFrame(provider, extension: "png"); - - // TODO: Assert metadata, frame count, etc + CurFrameMetadata meta = image.Frames[0].Metadata.GetCurMetadata(); + Assert.Equal(image.Width, meta.EncodingWidth); + Assert.Equal(image.Height, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs index 9908786f1..b9b66296d 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Cur/CurEncoderTests.cs @@ -3,6 +3,7 @@ using SixLabors.ImageSharp.Formats.Cur; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using static SixLabors.ImageSharp.Tests.TestImages.Cur; namespace SixLabors.ImageSharp.Tests.Formats.Icon.Cur; @@ -10,23 +11,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Icon.Cur; [Trait("Format", "Cur")] public class CurEncoderTests { - private static CurEncoder CurEncoder => new(); - - public static readonly TheoryData Files = new() - { - { WindowsMouse }, - }; + private static CurEncoder Encoder => new(); [Theory] - [MemberData(nameof(Files))] - public void Encode(string imagePath) + [WithFile(CurReal, PixelTypes.Rgba32)] + [WithFile(WindowsMouse, PixelTypes.Rgba32)] + public void CanRoundTripEncoder(TestImageProvider provider) + where TPixel : unmanaged, IPixel { - TestFile testFile = TestFile.Create(imagePath); - using Image input = testFile.CreateRgba32Image(); + using Image image = provider.GetImage(CurDecoder.Instance); using MemoryStream memStream = new(); - input.Save(memStream, CurEncoder); + image.DebugSaveMultiFrame(provider); + image.Save(memStream, Encoder); memStream.Seek(0, SeekOrigin.Begin); - CurDecoder.Instance.Decode(new(), memStream); + + using Image encoded = Image.Load(memStream); + encoded.DebugSaveMultiFrame(provider, appendPixelTypeToFileName: false); + + encoded.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, CurDecoder.Instance); } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs index 1c724ca35..a776a637b 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoDecoderTests.cs @@ -1,7 +1,9 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Ico; +using SixLabors.ImageSharp.Formats.Icon; using SixLabors.ImageSharp.PixelFormats; using static SixLabors.ImageSharp.Tests.TestImages.Ico; @@ -17,9 +19,9 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSaveMultiFrame(provider); - // TODO: Assert metadata, frame count, etc + Assert.Equal(10, image.Frames.Count); } [Theory] @@ -45,9 +47,16 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSave(provider); - // TODO: Assert metadata, frame count, etc + IcoFrameMetadata meta = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + int expectedWidth = image.Width >= 256 ? 0 : image.Width; + int expectedHeight = image.Height >= 256 ? 0 : image.Height; + + Assert.Equal(expectedWidth, meta.EncodingWidth); + Assert.Equal(expectedHeight, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel1, meta.BmpBitsPerPixel); } [Theory] @@ -74,9 +83,16 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSave(provider); + + IcoFrameMetadata meta = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + int expectedWidth = image.Width >= 256 ? 0 : image.Width; + int expectedHeight = image.Height >= 256 ? 0 : image.Height; - // TODO: Assert metadata, frame count, etc + Assert.Equal(expectedWidth, meta.EncodingWidth); + Assert.Equal(expectedHeight, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel24, meta.BmpBitsPerPixel); } [Theory] @@ -103,9 +119,16 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSave(provider); - // TODO: Assert metadata, frame count, etc + IcoFrameMetadata meta = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + int expectedWidth = image.Width >= 256 ? 0 : image.Width; + int expectedHeight = image.Height >= 256 ? 0 : image.Height; + + Assert.Equal(expectedWidth, meta.EncodingWidth); + Assert.Equal(expectedHeight, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); } [Theory] @@ -131,9 +154,16 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSave(provider); + + IcoFrameMetadata meta = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + int expectedWidth = image.Width >= 256 ? 0 : image.Width; + int expectedHeight = image.Height >= 256 ? 0 : image.Height; - // TODO: Assert metadata, frame count, etc + Assert.Equal(expectedWidth, meta.EncodingWidth); + Assert.Equal(expectedHeight, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel4, meta.BmpBitsPerPixel); } [Theory] @@ -151,7 +181,8 @@ public class IcoDecoderTests [WithFile(Bpp8Size5x5, PixelTypes.Rgba32)] [WithFile(Bpp8Size6x6, PixelTypes.Rgba32)] [WithFile(Bpp8Size7x7, PixelTypes.Rgba32)] - [WithFile(Bpp8Size8x8, PixelTypes.Rgba32)] + + // [WithFile(Bpp8Size8x8, PixelTypes.Rgba32)] This is actually 24 bit. [WithFile(Bpp8Size9x9, PixelTypes.Rgba32)] [WithFile(Bpp8TranspNotSquare, PixelTypes.Rgba32)] [WithFile(Bpp8TranspPartial, PixelTypes.Rgba32)] @@ -159,9 +190,16 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSave(provider); + + IcoFrameMetadata meta = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + int expectedWidth = image.Width >= 256 ? 0 : image.Width; + int expectedHeight = image.Height >= 256 ? 0 : image.Height; - // TODO: Assert metadata, frame count, etc + Assert.Equal(expectedWidth, meta.EncodingWidth); + Assert.Equal(expectedHeight, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel8, meta.BmpBitsPerPixel); } [Theory] @@ -174,10 +212,6 @@ public class IcoDecoderTests => Assert.Throws(() => { using Image image = provider.GetImage(IcoDecoder.Instance); - - image.DebugSaveMultiFrame(provider, extension: "png"); - - // TODO: Assert metadata, frame count, etc }); [Theory] @@ -186,9 +220,16 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSave(provider); + + IcoFrameMetadata meta = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + int expectedWidth = image.Width >= 256 ? 0 : image.Width; + int expectedHeight = image.Height >= 256 ? 0 : image.Height; - // TODO: Assert metadata, frame count, etc + Assert.Equal(expectedWidth, meta.EncodingWidth); + Assert.Equal(expectedHeight, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Png, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); } [Theory] @@ -199,9 +240,9 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + Assert.True(image.Frames.Count > 1); - // TODO: Assert metadata, frame count, etc + image.DebugSaveMultiFrame(provider); } [Theory] @@ -215,9 +256,46 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + Assert.True(image.Frames.Count > 1); + + for (int i = 0; i < image.Frames.Count; i++) + { + ImageFrame frame = image.Frames[i]; + IcoFrameMetadata meta = frame.Metadata.GetIcoMetadata(); + Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); + } + + image.DebugSaveMultiFrame(provider); + } + + [Theory] + [WithFile(MultiSizeA, PixelTypes.Rgba32)] + [WithFile(MultiSizeB, PixelTypes.Rgba32)] + [WithFile(MultiSizeC, PixelTypes.Rgba32)] + [WithFile(MultiSizeD, PixelTypes.Rgba32)] + [WithFile(MultiSizeE, PixelTypes.Rgba32)] + [WithFile(MultiSizeF, PixelTypes.Rgba32)] + public void MultiSize_CanDecodeSingleFrame(TestImageProvider provider) + { + using Image image = provider.GetImage(IcoDecoder.Instance, new() { MaxFrames = 1 }); + Assert.Single(image.Frames); + } - // TODO: Assert metadata, frame count, etc + [Theory] + [InlineData(MultiSizeA)] + [InlineData(MultiSizeB)] + [InlineData(MultiSizeC)] + [InlineData(MultiSizeD)] + [InlineData(MultiSizeE)] + [InlineData(MultiSizeF)] + public void MultiSize_CanIdentifySingleFrame(string imagePath) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + + ImageInfo imageInfo = Image.Identify(new() { MaxFrames = 1 }, stream); + + Assert.Single(imageInfo.FrameMetadataCollection); } [Theory] @@ -229,9 +307,9 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + Assert.True(image.Frames.Count > 1); - // TODO: Assert metadata, frame count, etc + image.DebugSaveMultiFrame(provider); } [Theory] @@ -240,8 +318,15 @@ public class IcoDecoderTests { using Image image = provider.GetImage(IcoDecoder.Instance); - image.DebugSaveMultiFrame(provider, extension: "png"); + image.DebugSave(provider); + + IcoFrameMetadata meta = image.Frames.RootFrame.Metadata.GetIcoMetadata(); + int expectedWidth = image.Width >= 256 ? 0 : image.Width; + int expectedHeight = image.Height >= 256 ? 0 : image.Height; - // TODO: Assert metadata, frame count, etc + Assert.Equal(expectedWidth, meta.EncodingWidth); + Assert.Equal(expectedHeight, meta.EncodingHeight); + Assert.Equal(IconFrameCompression.Bmp, meta.Compression); + Assert.Equal(BmpBitsPerPixel.Pixel32, meta.BmpBitsPerPixel); } } diff --git a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs index 9a239bdd4..db28f9f70 100644 --- a/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Icon/Ico/IcoEncoderTests.cs @@ -3,6 +3,7 @@ using SixLabors.ImageSharp.Formats.Ico; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using static SixLabors.ImageSharp.Tests.TestImages.Ico; namespace SixLabors.ImageSharp.Tests.Formats.Icon.Ico; @@ -10,23 +11,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Icon.Ico; [Trait("Format", "Icon")] public class IcoEncoderTests { - private static IcoEncoder CurEncoder => new(); - - public static readonly TheoryData Files = new() - { - { Flutter }, - }; + private static IcoEncoder Encoder => new(); [Theory] - [MemberData(nameof(Files))] - public void Encode(string imagePath) + [WithFile(Flutter, PixelTypes.Rgba32)] + public void CanRoundTripEncoder(TestImageProvider provider) + where TPixel : unmanaged, IPixel { - TestFile testFile = TestFile.Create(imagePath); - using Image input = testFile.CreateRgba32Image(); + using Image image = provider.GetImage(IcoDecoder.Instance); using MemoryStream memStream = new(); - input.Save(memStream, CurEncoder); + image.DebugSaveMultiFrame(provider); + image.Save(memStream, Encoder); memStream.Seek(0, SeekOrigin.Begin); - IcoDecoder.Instance.Decode(new(), memStream); + + using Image encoded = Image.Load(memStream); + encoded.DebugSaveMultiFrame(provider, appendPixelTypeToFileName: false); + + // Despite preservation of the palette. The process can still be lossy + encoded.CompareToOriginalMultiFrame(provider, ImageComparer.TolerantPercentage(.23f), IcoDecoder.Instance); } } From 9f889f75e753e394de8839829d79d96d46c2a572 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 20 Jun 2024 22:55:12 +1000 Subject: [PATCH 75/79] Handle out of bounds Gif LZW max code --- src/ImageSharp/Formats/Gif/LzwDecoder.cs | 13 +++++++---- .../Formats/Gif/GifDecoderTests.cs | 23 +++++++++++-------- .../Formats/Gif/GifMetadataTests.cs | 20 ++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + ...2012BadMinCode_Rgba32_issue2012_drona1.png | 3 +++ .../00.png | 3 +++ .../01.png | 3 +++ .../02.png | 3 +++ .../03.png | 3 +++ .../04.png | 3 +++ .../05.png | 3 +++ .../06.png | 3 +++ .../07.png | 3 +++ tests/Images/Input/Gif/issues/issue_2743.gif | 3 +++ 14 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/00.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/01.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/02.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/03.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/04.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/05.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/06.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png create mode 100644 tests/Images/Input/Gif/issues/issue_2743.gif diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index 4d282f26c..19f646b59 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -19,6 +19,11 @@ internal sealed class LzwDecoder : IDisposable ///
private const int MaxStackSize = 4096; + /// + /// The maximum bits for a lzw code. + /// + private const int MaximumLzwBits = 12; + /// /// The null code. /// @@ -73,12 +78,12 @@ internal sealed class LzwDecoder : IDisposable // It is possible to specify a larger LZW minimum code size than the palette length in bits // which may leave a gap in the codes where no colors are assigned. // http://www.matthewflickinger.com/lab/whatsinagif/lzw_image_data.asp#lzw_compression - if (minCodeSize < 2 || clearCode > MaxStackSize) + if (minCodeSize < 2 || minCodeSize > MaximumLzwBits || clearCode > MaxStackSize) { // Don't attempt to decode the frame indices. // Theoretically we could determine a min code size from the length of the provided // color palette but we won't bother since the image is most likely corrupted. - GifThrowHelper.ThrowInvalidImageContentException("Gif Image does not contain a valid LZW minimum code."); + return; } // The resulting index table length. @@ -245,12 +250,12 @@ internal sealed class LzwDecoder : IDisposable // It is possible to specify a larger LZW minimum code size than the palette length in bits // which may leave a gap in the codes where no colors are assigned. // http://www.matthewflickinger.com/lab/whatsinagif/lzw_image_data.asp#lzw_compression - if (minCodeSize < 2 || clearCode > MaxStackSize) + if (minCodeSize < 2 || minCodeSize > MaximumLzwBits || clearCode > MaxStackSize) { // Don't attempt to decode the frame indices. // Theoretically we could determine a min code size from the length of the provided // color palette but we won't bother since the image is most likely corrupted. - GifThrowHelper.ThrowInvalidImageContentException("Gif Image does not contain a valid LZW minimum code."); + return; } int codeSize = minCodeSize + 1; diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 6399e1193..ee108b9d9 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -296,15 +296,9 @@ public class GifDecoderTests public void Issue2012BadMinCode(TestImageProvider provider) where TPixel : unmanaged, IPixel { - Exception ex = Record.Exception( - () => - { - using Image image = provider.GetImage(); - image.DebugSave(provider); - }); - - Assert.NotNull(ex); - Assert.Contains("Gif Image does not contain a valid LZW minimum code.", ex.Message); + using Image image = provider.GetImage(); + image.DebugSave(provider); + image.CompareToReferenceOutput(provider); } // https://bugzilla.mozilla.org/show_bug.cgi?id=55918 @@ -318,4 +312,15 @@ public class GifDecoderTests image.DebugSave(provider); image.CompareFirstFrameToReferenceOutput(ImageComparer.Exact, provider); } + + // https://github.com/SixLabors/ImageSharp/issues/2743 + [Theory] + [WithFile(TestImages.Gif.Issues.BadMaxLzwBits, PixelTypes.Rgba32)] + public void IssueTooLargeLzwBits(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); + } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs index fb4445cda..b4fd87755 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifMetadataTests.cs @@ -214,4 +214,24 @@ public class GifMetadataTests Assert.Equal(frameDelay, gifFrameMetadata.FrameDelay); Assert.Equal(disposalMethod, gifFrameMetadata.DisposalMethod); } + + [Theory] + [InlineData(TestImages.Gif.Issues.BadMaxLzwBits, 8)] + [InlineData(TestImages.Gif.Issues.Issue2012BadMinCode, 1)] + public void Identify_Frames_Bad_Lzw(string imagePath, int framesCount) + { + TestFile testFile = TestFile.Create(imagePath); + using MemoryStream stream = new(testFile.Bytes, false); + + ImageInfo imageInfo = Image.Identify(stream); + + Assert.NotNull(imageInfo); + GifMetadata gifMetadata = imageInfo.Metadata.GetGifMetadata(); + Assert.NotNull(gifMetadata); + + Assert.Equal(framesCount, imageInfo.FrameMetadataCollection.Count); + GifFrameMetadata gifFrameMetadata = imageInfo.FrameMetadataCollection[imageInfo.FrameMetadataCollection.Count - 1].GetGifMetadata(); + + Assert.NotNull(gifFrameMetadata); + } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index c6765c640..c528251b3 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -515,6 +515,7 @@ public static class TestImages public const string BadAppExtLength = "Gif/issues/issue405_badappextlength252.gif"; public const string BadAppExtLength_2 = "Gif/issues/issue405_badappextlength252-2.gif"; public const string BadDescriptorWidth = "Gif/issues/issue403_baddescriptorwidth.gif"; + public const string BadMaxLzwBits = "Gif/issues/issue_2743.gif"; public const string DeferredClearCode = "Gif/issues/bugzilla-55918.gif"; public const string Issue1505 = "Gif/issues/issue1505_argumentoutofrange.png"; public const string Issue1530 = "Gif/issues/issue1530.gif"; diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png new file mode 100644 index 000000000..cdba9277b --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a3a24c066895fd3a76649da376485cbc1912d6a3ae15369575f523e66364b3b6 +size 141563 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/00.png new file mode 100644 index 000000000..923fbc122 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:800d1ec2d7c7c99d449db1f49ef202cf18214016eae65ebc4216d6f4b1f4d328 +size 537 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/01.png new file mode 100644 index 000000000..6c2134d8b --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:94dcd97831b16165f3331e429d72d7ef546e04038cab754c7918f9cf535ff30a +size 542 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/02.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/02.png new file mode 100644 index 000000000..6f50397ea --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/02.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec1a589a8fae1b17a82b70a9583ea2ee012a476b1fa8fdba27fee2b7ce0403b2 +size 540 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/03.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/03.png new file mode 100644 index 000000000..82061ba0a --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/03.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c8751f4fafd5c56066dbb8d64a3890fc420a3bd66881a55e309ba274b6d14e4 +size 542 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/04.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/04.png new file mode 100644 index 000000000..8902eb824 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/04.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b78516c9874cb15de4c4b98ed307e8105d962fc6bfa7aa3490b2c7e13b455a2d +size 544 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/05.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/05.png new file mode 100644 index 000000000..82061ba0a --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/05.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c8751f4fafd5c56066dbb8d64a3890fc420a3bd66881a55e309ba274b6d14e4 +size 542 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/06.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/06.png new file mode 100644 index 000000000..6f50397ea --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/06.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec1a589a8fae1b17a82b70a9583ea2ee012a476b1fa8fdba27fee2b7ce0403b2 +size 540 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png new file mode 100644 index 000000000..75cf685e4 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:489642f0c81fd12e97007fe6feb11b0e93e351199a922ce038069a3782ad0722 +size 135 diff --git a/tests/Images/Input/Gif/issues/issue_2743.gif b/tests/Images/Input/Gif/issues/issue_2743.gif new file mode 100644 index 000000000..4ce61340d --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue_2743.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4be51cb9c258a6518d791ad2810fa0d71449805a5d5a8f95dcc7da2dc558ed73 +size 166413 From e4ba5d30d770c24b23cfc8023ffdad2511cb15d1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 27 Jun 2024 15:33:59 +1000 Subject: [PATCH 76/79] Decode LZW row by row --- src/ImageSharp/Formats/Gif/GifDecoderCore.cs | 196 +++++------ src/ImageSharp/Formats/Gif/LzwDecoder.cs | 330 ++++++++---------- .../Formats/Gif/GifDecoderTests.cs | 11 + tests/ImageSharp.Tests/TestImages.cs | 1 + ...2012BadMinCode_Rgba32_issue2012_drona1.png | 4 +- .../00.png | 3 + .../01.png | 3 + .../07.png | 4 +- tests/Images/Input/Gif/issues/issue_2758.gif | 3 + 9 files changed, 266 insertions(+), 289 deletions(-) create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/00.png create mode 100644 tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/01.png create mode 100644 tests/Images/Input/Gif/issues/issue_2758.gif diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index aecbbbbc7..b62b0f67b 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -427,68 +427,49 @@ internal sealed class GifDecoderCore : IImageDecoderInternals { this.ReadImageDescriptor(stream); - Buffer2D? indices = null; - try - { - // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table. - bool hasLocalColorTable = this.imageDescriptor.LocalColorTableFlag; - - if (hasLocalColorTable) - { - // Read and store the local color table. We allocate the maximum possible size and slice to match. - int length = this.currentLocalColorTableSize = this.imageDescriptor.LocalColorTableSize * 3; - this.currentLocalColorTable ??= this.configuration.MemoryAllocator.Allocate(768, AllocationOptions.Clean); - stream.Read(this.currentLocalColorTable.GetSpan()[..length]); - } - - indices = this.configuration.MemoryAllocator.Allocate2D(this.imageDescriptor.Width, this.imageDescriptor.Height, AllocationOptions.Clean); - this.ReadFrameIndices(stream, indices); + // Determine the color table for this frame. If there is a local one, use it otherwise use the global color table. + bool hasLocalColorTable = this.imageDescriptor.LocalColorTableFlag; - Span rawColorTable = default; - if (hasLocalColorTable) - { - rawColorTable = this.currentLocalColorTable!.GetSpan()[..this.currentLocalColorTableSize]; - } - else if (this.globalColorTable != null) - { - rawColorTable = this.globalColorTable.GetSpan(); - } - - ReadOnlySpan colorTable = MemoryMarshal.Cast(rawColorTable); - this.ReadFrameColors(ref image, ref previousFrame, indices, colorTable, this.imageDescriptor); + if (hasLocalColorTable) + { + // Read and store the local color table. We allocate the maximum possible size and slice to match. + int length = this.currentLocalColorTableSize = this.imageDescriptor.LocalColorTableSize * 3; + this.currentLocalColorTable ??= this.configuration.MemoryAllocator.Allocate(768, AllocationOptions.Clean); + stream.Read(this.currentLocalColorTable.GetSpan()[..length]); + } - // Skip any remaining blocks - SkipBlock(stream); + Span rawColorTable = default; + if (hasLocalColorTable) + { + rawColorTable = this.currentLocalColorTable!.GetSpan()[..this.currentLocalColorTableSize]; } - finally + else if (this.globalColorTable != null) { - indices?.Dispose(); + rawColorTable = this.globalColorTable.GetSpan(); } - } - /// - /// Reads the frame indices marking the color to use for each pixel. - /// - /// The containing image data. - /// The 2D pixel buffer to write to. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ReadFrameIndices(BufferedReadStream stream, Buffer2D indices) - { - int minCodeSize = stream.ReadByte(); - using LzwDecoder lzwDecoder = new(this.configuration.MemoryAllocator, stream); - lzwDecoder.DecodePixels(minCodeSize, indices); + ReadOnlySpan colorTable = MemoryMarshal.Cast(rawColorTable); + this.ReadFrameColors(stream, ref image, ref previousFrame, colorTable, this.imageDescriptor); + + // Skip any remaining blocks + SkipBlock(stream); } /// /// Reads the frames colors, mapping indices to colors. /// /// The pixel format. + /// The containing image data. /// The image to decode the information to. /// The previous frame. - /// The indexed pixels. /// The color table containing the available colors. /// The - private void ReadFrameColors(ref Image? image, ref ImageFrame? previousFrame, Buffer2D indices, ReadOnlySpan colorTable, in GifImageDescriptor descriptor) + private void ReadFrameColors( + BufferedReadStream stream, + ref Image? image, + ref ImageFrame? previousFrame, + ReadOnlySpan colorTable, + in GifImageDescriptor descriptor) where TPixel : unmanaged, IPixel { int imageWidth = this.logicalScreenDescriptor.Width; @@ -549,73 +530,83 @@ internal sealed class GifDecoderCore : IImageDecoderInternals byte transIndex = this.graphicsControlExtension.TransparencyIndex; int colorTableMaxIdx = colorTable.Length - 1; - for (int y = descriptorTop; y < descriptorBottom && y < imageHeight; y++) + // For a properly encoded gif the descriptor dimensions will never exceed the logical screen dimensions. + // However we have images that exceed this that can be decoded by other libraries. #1530 + using IMemoryOwner indicesRowOwner = this.memoryAllocator.Allocate(descriptor.Width); + Span indicesRow = indicesRowOwner.Memory.Span; + ref byte indicesRowRef = ref MemoryMarshal.GetReference(indicesRow); + + int minCodeSize = stream.ReadByte(); + if (LzwDecoder.IsValidMinCodeSize(minCodeSize)) { - ref byte indicesRowRef = ref MemoryMarshal.GetReference(indices.DangerousGetRowSpan(y - descriptorTop)); + using LzwDecoder lzwDecoder = new(this.configuration.MemoryAllocator, stream, minCodeSize); - // Check if this image is interlaced. - int writeY; // the target y offset to write to - if (descriptor.InterlaceFlag) + for (int y = descriptorTop; y < descriptorBottom && y < imageHeight; y++) { - // If so then we read lines at predetermined offsets. - // When an entire image height worth of offset lines has been read we consider this a pass. - // With each pass the number of offset lines changes and the starting line changes. - if (interlaceY >= descriptor.Height) + // Check if this image is interlaced. + int writeY; // the target y offset to write to + if (descriptor.InterlaceFlag) { - interlacePass++; - switch (interlacePass) + // If so then we read lines at predetermined offsets. + // When an entire image height worth of offset lines has been read we consider this a pass. + // With each pass the number of offset lines changes and the starting line changes. + if (interlaceY >= descriptor.Height) { - case 1: - interlaceY = 4; - break; - case 2: - interlaceY = 2; - interlaceIncrement = 4; - break; - case 3: - interlaceY = 1; - interlaceIncrement = 2; - break; + interlacePass++; + switch (interlacePass) + { + case 1: + interlaceY = 4; + break; + case 2: + interlaceY = 2; + interlaceIncrement = 4; + break; + case 3: + interlaceY = 1; + interlaceIncrement = 2; + break; + } } - } - writeY = interlaceY + descriptor.Top; - interlaceY += interlaceIncrement; - } - else - { - writeY = y; - } + writeY = Math.Min(interlaceY + descriptor.Top, image.Height); + interlaceY += interlaceIncrement; + } + else + { + writeY = y; + } - ref TPixel rowRef = ref MemoryMarshal.GetReference(imageFrame.PixelBuffer.DangerousGetRowSpan(writeY)); + lzwDecoder.DecodePixelRow(indicesRow); + ref TPixel rowRef = ref MemoryMarshal.GetReference(imageFrame.PixelBuffer.DangerousGetRowSpan(writeY)); - if (!transFlag) - { - // #403 The left + width value can be larger than the image width - for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++) + if (!transFlag) { - int index = Numerics.Clamp(Unsafe.Add(ref indicesRowRef, (uint)(x - descriptorLeft)), 0, colorTableMaxIdx); - ref TPixel pixel = ref Unsafe.Add(ref rowRef, (uint)x); - Rgb24 rgb = colorTable[index]; - pixel.FromRgb24(rgb); + // #403 The left + width value can be larger than the image width + for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++) + { + int index = Numerics.Clamp(Unsafe.Add(ref indicesRowRef, (uint)(x - descriptorLeft)), 0, colorTableMaxIdx); + ref TPixel pixel = ref Unsafe.Add(ref rowRef, (uint)x); + Rgb24 rgb = colorTable[index]; + pixel.FromRgb24(rgb); + } } - } - else - { - for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++) + else { - int rawIndex = Unsafe.Add(ref indicesRowRef, (uint)(x - descriptorLeft)); - - // Treat any out of bounds values as transparent. - if (rawIndex > colorTableMaxIdx || rawIndex == transIndex) + for (int x = descriptorLeft; x < descriptorRight && x < imageWidth; x++) { - continue; - } + int index = Unsafe.Add(ref indicesRowRef, (uint)(x - descriptorLeft)); + + // Treat any out of bounds values as transparent. + if (index > colorTableMaxIdx || index == transIndex) + { + continue; + } - int index = Numerics.Clamp(rawIndex, 0, colorTableMaxIdx); - ref TPixel pixel = ref Unsafe.Add(ref rowRef, (uint)x); - Rgb24 rgb = colorTable[index]; - pixel.FromRgb24(rgb); + ref TPixel pixel = ref Unsafe.Add(ref rowRef, (uint)x); + Rgb24 rgb = colorTable[index]; + pixel.FromRgb24(rgb); + } } } } @@ -656,8 +647,11 @@ internal sealed class GifDecoderCore : IImageDecoderInternals // Skip the frame indices. Pixels length + mincode size. // The gif format does not tell us the length of the compressed data beforehand. int minCodeSize = stream.ReadByte(); - using LzwDecoder lzwDecoder = new(this.configuration.MemoryAllocator, stream); - lzwDecoder.SkipIndices(minCodeSize, this.imageDescriptor.Width * this.imageDescriptor.Height); + if (LzwDecoder.IsValidMinCodeSize(minCodeSize)) + { + using LzwDecoder lzwDecoder = new(this.configuration.MemoryAllocator, stream, minCodeSize); + lzwDecoder.SkipIndices(this.imageDescriptor.Width * this.imageDescriptor.Height); + } ImageFrameMetadata currentFrame = new(); frameMetadata.Add(currentFrame); diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs index 19f646b59..ec33f2b3e 100644 --- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs +++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs @@ -44,10 +44,29 @@ internal sealed class LzwDecoder : IDisposable ///
private readonly IMemoryOwner suffix; + /// + /// The scratch buffer for reading data blocks. + /// + private readonly IMemoryOwner scratchBuffer; + /// /// The pixel stack buffer. /// private readonly IMemoryOwner pixelStack; + private readonly int minCodeSize; + private readonly int clearCode; + private readonly int endCode; + private int code; + private int codeSize; + private int codeMask; + private int availableCode; + private int oldCode = NullCode; + private int bits; + private int top; + private int count; + private int bufferIndex; + private int data; + private int first; /// /// Initializes a new instance of the class @@ -55,335 +74,277 @@ internal sealed class LzwDecoder : IDisposable /// /// The to use for buffer allocations. /// The stream to read from. + /// The minimum code size. /// is null. - public LzwDecoder(MemoryAllocator memoryAllocator, BufferedReadStream stream) + public LzwDecoder(MemoryAllocator memoryAllocator, BufferedReadStream stream, int minCodeSize) { this.stream = stream ?? throw new ArgumentNullException(nameof(stream)); this.prefix = memoryAllocator.Allocate(MaxStackSize, AllocationOptions.Clean); this.suffix = memoryAllocator.Allocate(MaxStackSize, AllocationOptions.Clean); this.pixelStack = memoryAllocator.Allocate(MaxStackSize + 1, AllocationOptions.Clean); + this.scratchBuffer = memoryAllocator.Allocate(byte.MaxValue, AllocationOptions.None); + this.minCodeSize = minCodeSize; + + // Calculate the clear code. The value of the clear code is 2 ^ minCodeSize + this.clearCode = 1 << minCodeSize; + this.codeSize = minCodeSize + 1; + this.codeMask = (1 << this.codeSize) - 1; + this.endCode = this.clearCode + 1; + this.availableCode = this.clearCode + 2; + + ref int suffixRef = ref MemoryMarshal.GetReference(this.suffix.GetSpan()); + for (this.code = 0; this.code < this.clearCode; this.code++) + { + Unsafe.Add(ref suffixRef, (uint)this.code) = (byte)this.code; + } } /// - /// Decodes and decompresses all pixel indices from the stream, assigning the pixel values to the buffer. + /// Gets a value indicating whether the minimum code size is valid. /// - /// Minimum code size of the data. - /// The pixel array to decode to. - public void DecodePixels(int minCodeSize, Buffer2D pixels) + /// The minimum code size. + /// + /// if the minimum code size is valid; otherwise, . + /// + public static bool IsValidMinCodeSize(int minCodeSize) { - // Calculate the clear code. The value of the clear code is 2 ^ minCodeSize - int clearCode = 1 << minCodeSize; - // It is possible to specify a larger LZW minimum code size than the palette length in bits // which may leave a gap in the codes where no colors are assigned. // http://www.matthewflickinger.com/lab/whatsinagif/lzw_image_data.asp#lzw_compression + int clearCode = 1 << minCodeSize; if (minCodeSize < 2 || minCodeSize > MaximumLzwBits || clearCode > MaxStackSize) { // Don't attempt to decode the frame indices. // Theoretically we could determine a min code size from the length of the provided // color palette but we won't bother since the image is most likely corrupted. - return; + return false; } - // The resulting index table length. - int width = pixels.Width; - int height = pixels.Height; - int length = width * height; - - int codeSize = minCodeSize + 1; - - // Calculate the end code - int endCode = clearCode + 1; - - // Calculate the available code. - int availableCode = clearCode + 2; - - // Jillzhangs Code see: http://giflib.codeplex.com/ - // Adapted from John Cristy's ImageMagick. - int code; - int oldCode = NullCode; - int codeMask = (1 << codeSize) - 1; - int bits = 0; - - int top = 0; - int count = 0; - int bi = 0; - int xyz = 0; + return true; + } - int data = 0; - int first = 0; + /// + /// Decodes and decompresses all pixel indices for a single row from the stream, assigning the pixel values to the buffer. + /// + /// The pixel indices array to decode to. + public void DecodePixelRow(Span indices) + { + indices.Clear(); + ref byte pixelsRowRef = ref MemoryMarshal.GetReference(indices); ref int prefixRef = ref MemoryMarshal.GetReference(this.prefix.GetSpan()); ref int suffixRef = ref MemoryMarshal.GetReference(this.suffix.GetSpan()); ref int pixelStackRef = ref MemoryMarshal.GetReference(this.pixelStack.GetSpan()); + Span buffer = this.scratchBuffer.GetSpan(); - for (code = 0; code < clearCode; code++) - { - Unsafe.Add(ref suffixRef, (uint)code) = (byte)code; - } - - Span buffer = stackalloc byte[byte.MaxValue]; - - int y = 0; int x = 0; - int rowMax = width; - ref byte pixelsRowRef = ref MemoryMarshal.GetReference(pixels.DangerousGetRowSpan(y)); - while (xyz < length) + int xyz = 0; + while (xyz < indices.Length) { - // Reset row reference. - if (xyz == rowMax) - { - x = 0; - pixelsRowRef = ref MemoryMarshal.GetReference(pixels.DangerousGetRowSpan(++y)); - rowMax = (y * width) + width; - } - - if (top == 0) + if (this.top == 0) { - if (bits < codeSize) + if (this.bits < this.codeSize) { // Load bytes until there are enough bits for a code. - if (count == 0) + if (this.count == 0) { // Read a new data block. - count = this.ReadBlock(buffer); - if (count == 0) + this.count = this.ReadBlock(buffer); + if (this.count == 0) { break; } - bi = 0; + this.bufferIndex = 0; } - data += buffer[bi] << bits; + this.data += buffer[this.bufferIndex] << this.bits; - bits += 8; - bi++; - count--; + this.bits += 8; + this.bufferIndex++; + this.count--; continue; } // Get the next code - code = data & codeMask; - data >>= codeSize; - bits -= codeSize; + this.code = this.data & this.codeMask; + this.data >>= this.codeSize; + this.bits -= this.codeSize; // Interpret the code - if (code > availableCode || code == endCode) + if (this.code > this.availableCode || this.code == this.endCode) { break; } - if (code == clearCode) + if (this.code == this.clearCode) { // Reset the decoder - codeSize = minCodeSize + 1; - codeMask = (1 << codeSize) - 1; - availableCode = clearCode + 2; - oldCode = NullCode; + this.codeSize = this.minCodeSize + 1; + this.codeMask = (1 << this.codeSize) - 1; + this.availableCode = this.clearCode + 2; + this.oldCode = NullCode; continue; } - if (oldCode == NullCode) + if (this.oldCode == NullCode) { - Unsafe.Add(ref pixelStackRef, (uint)top++) = Unsafe.Add(ref suffixRef, (uint)code); - oldCode = code; - first = code; + Unsafe.Add(ref pixelStackRef, (uint)this.top++) = Unsafe.Add(ref suffixRef, (uint)this.code); + this.oldCode = this.code; + this.first = this.code; continue; } - int inCode = code; - if (code == availableCode) + int inCode = this.code; + if (this.code == this.availableCode) { - Unsafe.Add(ref pixelStackRef, (uint)top++) = (byte)first; + Unsafe.Add(ref pixelStackRef, (uint)this.top++) = (byte)this.first; - code = oldCode; + this.code = this.oldCode; } - while (code > clearCode) + while (this.code > this.clearCode) { - Unsafe.Add(ref pixelStackRef, (uint)top++) = Unsafe.Add(ref suffixRef, (uint)code); - code = Unsafe.Add(ref prefixRef, (uint)code); + Unsafe.Add(ref pixelStackRef, (uint)this.top++) = Unsafe.Add(ref suffixRef, (uint)this.code); + this.code = Unsafe.Add(ref prefixRef, (uint)this.code); } - int suffixCode = Unsafe.Add(ref suffixRef, (uint)code); - first = suffixCode; - Unsafe.Add(ref pixelStackRef, (uint)top++) = suffixCode; + int suffixCode = Unsafe.Add(ref suffixRef, (uint)this.code); + this.first = suffixCode; + Unsafe.Add(ref pixelStackRef, (uint)this.top++) = suffixCode; // Fix for Gifs that have "deferred clear code" as per here : // https://bugzilla.mozilla.org/show_bug.cgi?id=55918 - if (availableCode < MaxStackSize) + if (this.availableCode < MaxStackSize) { - Unsafe.Add(ref prefixRef, (uint)availableCode) = oldCode; - Unsafe.Add(ref suffixRef, (uint)availableCode) = first; - availableCode++; - if (availableCode == codeMask + 1 && availableCode < MaxStackSize) + Unsafe.Add(ref prefixRef, (uint)this.availableCode) = this.oldCode; + Unsafe.Add(ref suffixRef, (uint)this.availableCode) = this.first; + this.availableCode++; + if (this.availableCode == this.codeMask + 1 && this.availableCode < MaxStackSize) { - codeSize++; - codeMask = (1 << codeSize) - 1; + this.codeSize++; + this.codeMask = (1 << this.codeSize) - 1; } } - oldCode = inCode; + this.oldCode = inCode; } // Pop a pixel off the pixel stack. - top--; + this.top--; // Clear missing pixels xyz++; - Unsafe.Add(ref pixelsRowRef, (uint)x++) = (byte)Unsafe.Add(ref pixelStackRef, (uint)top); + Unsafe.Add(ref pixelsRowRef, (uint)x++) = (byte)Unsafe.Add(ref pixelStackRef, (uint)this.top); } } /// /// Decodes and decompresses all pixel indices from the stream allowing skipping of the data. /// - /// Minimum code size of the data. /// The resulting index table length. - public void SkipIndices(int minCodeSize, int length) + public void SkipIndices(int length) { - // Calculate the clear code. The value of the clear code is 2 ^ minCodeSize - int clearCode = 1 << minCodeSize; - - // It is possible to specify a larger LZW minimum code size than the palette length in bits - // which may leave a gap in the codes where no colors are assigned. - // http://www.matthewflickinger.com/lab/whatsinagif/lzw_image_data.asp#lzw_compression - if (minCodeSize < 2 || minCodeSize > MaximumLzwBits || clearCode > MaxStackSize) - { - // Don't attempt to decode the frame indices. - // Theoretically we could determine a min code size from the length of the provided - // color palette but we won't bother since the image is most likely corrupted. - return; - } - - int codeSize = minCodeSize + 1; - - // Calculate the end code - int endCode = clearCode + 1; - - // Calculate the available code. - int availableCode = clearCode + 2; - - // Jillzhangs Code see: http://giflib.codeplex.com/ - // Adapted from John Cristy's ImageMagick. - int code; - int oldCode = NullCode; - int codeMask = (1 << codeSize) - 1; - int bits = 0; - - int top = 0; - int count = 0; - int bi = 0; - int xyz = 0; - - int data = 0; - int first = 0; - ref int prefixRef = ref MemoryMarshal.GetReference(this.prefix.GetSpan()); ref int suffixRef = ref MemoryMarshal.GetReference(this.suffix.GetSpan()); ref int pixelStackRef = ref MemoryMarshal.GetReference(this.pixelStack.GetSpan()); + Span buffer = this.scratchBuffer.GetSpan(); - for (code = 0; code < clearCode; code++) - { - Unsafe.Add(ref suffixRef, (uint)code) = (byte)code; - } - - Span buffer = stackalloc byte[byte.MaxValue]; + int xyz = 0; while (xyz < length) { - if (top == 0) + if (this.top == 0) { - if (bits < codeSize) + if (this.bits < this.codeSize) { // Load bytes until there are enough bits for a code. - if (count == 0) + if (this.count == 0) { // Read a new data block. - count = this.ReadBlock(buffer); - if (count == 0) + this.count = this.ReadBlock(buffer); + if (this.count == 0) { break; } - bi = 0; + this.bufferIndex = 0; } - data += buffer[bi] << bits; + this.data += buffer[this.bufferIndex] << this.bits; - bits += 8; - bi++; - count--; + this.bits += 8; + this.bufferIndex++; + this.count--; continue; } // Get the next code - code = data & codeMask; - data >>= codeSize; - bits -= codeSize; + this.code = this.data & this.codeMask; + this.data >>= this.codeSize; + this.bits -= this.codeSize; // Interpret the code - if (code > availableCode || code == endCode) + if (this.code > this.availableCode || this.code == this.endCode) { break; } - if (code == clearCode) + if (this.code == this.clearCode) { // Reset the decoder - codeSize = minCodeSize + 1; - codeMask = (1 << codeSize) - 1; - availableCode = clearCode + 2; - oldCode = NullCode; + this.codeSize = this.minCodeSize + 1; + this.codeMask = (1 << this.codeSize) - 1; + this.availableCode = this.clearCode + 2; + this.oldCode = NullCode; continue; } - if (oldCode == NullCode) + if (this.oldCode == NullCode) { - Unsafe.Add(ref pixelStackRef, (uint)top++) = Unsafe.Add(ref suffixRef, (uint)code); - oldCode = code; - first = code; + Unsafe.Add(ref pixelStackRef, (uint)this.top++) = Unsafe.Add(ref suffixRef, (uint)this.code); + this.oldCode = this.code; + this.first = this.code; continue; } - int inCode = code; - if (code == availableCode) + int inCode = this.code; + if (this.code == this.availableCode) { - Unsafe.Add(ref pixelStackRef, (uint)top++) = (byte)first; + Unsafe.Add(ref pixelStackRef, (uint)this.top++) = (byte)this.first; - code = oldCode; + this.code = this.oldCode; } - while (code > clearCode) + while (this.code > this.clearCode) { - Unsafe.Add(ref pixelStackRef, (uint)top++) = Unsafe.Add(ref suffixRef, (uint)code); - code = Unsafe.Add(ref prefixRef, (uint)code); + Unsafe.Add(ref pixelStackRef, (uint)this.top++) = Unsafe.Add(ref suffixRef, (uint)this.code); + this.code = Unsafe.Add(ref prefixRef, (uint)this.code); } - int suffixCode = Unsafe.Add(ref suffixRef, (uint)code); - first = suffixCode; - Unsafe.Add(ref pixelStackRef, (uint)top++) = suffixCode; + int suffixCode = Unsafe.Add(ref suffixRef, (uint)this.code); + this.first = suffixCode; + Unsafe.Add(ref pixelStackRef, (uint)this.top++) = suffixCode; // Fix for Gifs that have "deferred clear code" as per here : // https://bugzilla.mozilla.org/show_bug.cgi?id=55918 - if (availableCode < MaxStackSize) + if (this.availableCode < MaxStackSize) { - Unsafe.Add(ref prefixRef, (uint)availableCode) = oldCode; - Unsafe.Add(ref suffixRef, (uint)availableCode) = first; - availableCode++; - if (availableCode == codeMask + 1 && availableCode < MaxStackSize) + Unsafe.Add(ref prefixRef, (uint)this.availableCode) = this.oldCode; + Unsafe.Add(ref suffixRef, (uint)this.availableCode) = this.first; + this.availableCode++; + if (this.availableCode == this.codeMask + 1 && this.availableCode < MaxStackSize) { - codeSize++; - codeMask = (1 << codeSize) - 1; + this.codeSize++; + this.codeMask = (1 << this.codeSize) - 1; } } - oldCode = inCode; + this.oldCode = inCode; } // Pop a pixel off the pixel stack. - top--; + this.top--; // Clear missing pixels xyz++; @@ -419,5 +380,6 @@ internal sealed class LzwDecoder : IDisposable this.prefix.Dispose(); this.suffix.Dispose(); this.pixelStack.Dispose(); + this.scratchBuffer.Dispose(); } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index ee108b9d9..f4e6487a5 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -204,6 +204,17 @@ public class GifDecoderTests image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); } + // https://github.com/SixLabors/ImageSharp/issues/2758 + [Theory] + [WithFile(TestImages.Gif.Issues.Issue2758, PixelTypes.Rgba32)] + public void Issue2758_BadDescriptorDimensions(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); + } + // https://github.com/SixLabors/ImageSharp/issues/405 [Theory] [WithFile(TestImages.Gif.Issues.BadAppExtLength, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index c528251b3..879fed9d2 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -530,6 +530,7 @@ public static class TestImages public const string Issue2450_A = "Gif/issues/issue_2450.gif"; public const string Issue2450_B = "Gif/issues/issue_2450_2.gif"; public const string Issue2198 = "Gif/issues/issue_2198.gif"; + public const string Issue2758 = "Gif/issues/issue_2758.gif"; } public static readonly string[] Animated = diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png index cdba9277b..b07e80662 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2012BadMinCode_Rgba32_issue2012_drona1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3a24c066895fd3a76649da376485cbc1912d6a3ae15369575f523e66364b3b6 -size 141563 +oid sha256:588d055a93c7b4fdb62e8b77f3ae08753a9e8990151cb0523f5e761996189b70 +size 142244 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/00.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/00.png new file mode 100644 index 000000000..f63cc98ad --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/00.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f39b23217f1d095eeb8eed5ccea36be813c307a60ef4b1942e9f74028451c38 +size 81944 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/01.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/01.png new file mode 100644 index 000000000..f63cc98ad --- /dev/null +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/Issue2758_BadDescriptorDimensions_Rgba32_issue_2758.gif/01.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f39b23217f1d095eeb8eed5ccea36be813c307a60ef4b1942e9f74028451c38 +size 81944 diff --git a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png index 75cf685e4..efba40c99 100644 --- a/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png +++ b/tests/Images/External/ReferenceOutput/GifDecoderTests/IssueTooLargeLzwBits_Rgba32_issue_2743.gif/07.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:489642f0c81fd12e97007fe6feb11b0e93e351199a922ce038069a3782ad0722 -size 135 +oid sha256:5016a323018f09e292165ad5392d82dcbad5e79c2b6b93aff3322dffff80b309 +size 126 diff --git a/tests/Images/Input/Gif/issues/issue_2758.gif b/tests/Images/Input/Gif/issues/issue_2758.gif new file mode 100644 index 000000000..17db9fa13 --- /dev/null +++ b/tests/Images/Input/Gif/issues/issue_2758.gif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13e9374181c7536d1d2ecb514753a5290c0ec06234ca079c6c8c8a832586b668 +size 199 From fae857fb1cd3f97df9831b9bc06ac2c33f2245b6 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 28 Jun 2024 13:46:47 +1000 Subject: [PATCH 77/79] Clamp JPEG quality estimation results. --- .../Formats/Jpeg/Components/Quantization.cs | 2 +- .../Formats/Jpg/JpegDecoderTests.Metadata.cs | 27 +++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Jpg/issues/issue-2758.jpg | 3 +++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/Images/Input/Jpg/issues/issue-2758.jpg diff --git a/src/ImageSharp/Formats/Jpeg/Components/Quantization.cs b/src/ImageSharp/Formats/Jpeg/Components/Quantization.cs index 3d53d5bfe..7dca1cf5e 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Quantization.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Quantization.cs @@ -146,7 +146,7 @@ internal static class Quantization quality = (int)Math.Round(5000.0 / sumPercent); } - return quality; + return Numerics.Clamp(quality, MinQualityFactor, MaxQualityFactor); } /// diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index 1c203e734..e68dd1f87 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities; // ReSharper disable InconsistentNaming @@ -425,6 +426,32 @@ public partial class JpegDecoderTests VerifyEncodedStrings(exif); } + // https://github.com/SixLabors/ImageSharp/issues/2758 + [Theory] + [WithFile(TestImages.Jpeg.Issues.Issue2758, PixelTypes.L8)] + public void Issue2758_DecodeWorks(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(JpegDecoder.Instance); + + Assert.Equal(59787, image.Width); + Assert.Equal(511, image.Height); + + JpegMetadata meta = image.Metadata.GetJpegMetadata(); + + // Quality determination should be between 1-100. + Assert.Equal(15, meta.LuminanceQuality); + Assert.Equal(1, meta.ChrominanceQuality); + + // We want to test the encoder to ensure the determined values can be encoded but not by encoding + // the full size image as it would be too slow. + // We will crop the image to a smaller size and then encode it. + image.Mutate(x => x.Crop(new(0, 0, 100, 100))); + + using MemoryStream ms = new(); + image.Save(ms, new JpegEncoder()); + } + private static void VerifyEncodedStrings(ExifProfile exif) { Assert.NotNull(exif); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 879fed9d2..c571619d6 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -320,6 +320,7 @@ public static class TestImages public const string HangBadScan = "Jpg/issues/Hang_C438A851.jpg"; public const string Issue2517 = "Jpg/issues/issue2517-bad-d7.jpg"; public const string Issue2638 = "Jpg/issues/Issue2638.jpg"; + public const string Issue2758 = "Jpg/issues/issue-2758.jpg"; public static class Fuzz { diff --git a/tests/Images/Input/Jpg/issues/issue-2758.jpg b/tests/Images/Input/Jpg/issues/issue-2758.jpg new file mode 100644 index 000000000..48ee1159e --- /dev/null +++ b/tests/Images/Input/Jpg/issues/issue-2758.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f32a238b57b7073f7442f8ae7efd6ba3ae4cda30d57e6666fb8a1eaa27108558 +size 1412 From 0103d81d96925a3c7a45fba8e27e042b8e431035 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 29 Jun 2024 16:32:24 +1000 Subject: [PATCH 78/79] Fix off-by-one error when centering a transform. --- .../Processing/AffineTransformBuilder.cs | 73 ++++++-- .../Transforms/TransformExtensions.cs | 4 +- .../Linear/LinearTransformUtility.cs | 8 +- .../Transforms/Linear/RotateProcessor.cs | 7 +- .../Transforms/Linear/SkewProcessor.cs | 7 +- .../Processors/Transforms/TransformUtils.cs | 159 +++++++++++++----- .../Processing/ProjectiveTransformBuilder.cs | 69 ++++++-- .../Transforms/AffineTransformTests.cs | 28 +++ .../Transforms/ProjectiveTransformTests.cs | 28 +++ .../Transforms/TransformBuilderTestBase.cs | 4 +- ...stPattern100x50_R(0)_S(1,1)_T(-20,-10).png | 4 +- ...2_TestPattern100x50_R(0)_S(1,1)_T(0,0).png | 4 +- ...TestPattern100x50_R(0)_S(1,1)_T(20,10).png | 4 +- ...2_TestPattern100x50_R(0)_S(1,2)_T(0,0).png | 4 +- ...2_TestPattern100x50_R(0)_S(2,1)_T(0,0).png | 4 +- ...tPattern100x50_R(50)_S(1,1)_T(-20,-10).png | 4 +- ..._TestPattern100x50_R(50)_S(1,1)_T(0,0).png | 4 +- ...estPattern100x50_R(50)_S(1,1)_T(20,10).png | 4 +- ...ttern100x50_R(50)_S(1.1,1.3)_T(30,-20).png | 4 +- ...tPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png | 4 +- ...ate_Rgba32_TestPattern100x50__original.png | 4 +- ...d_Rgba32_TestPattern96x96_R(50)_S(0.8).png | 4 +- ...pler_Rgba32_TestPattern150x150_Bicubic.png | 4 +- ...hSampler_Rgba32_TestPattern150x150_Box.png | 4 +- ...r_Rgba32_TestPattern150x150_CatmullRom.png | 4 +- ...pler_Rgba32_TestPattern150x150_Hermite.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos2.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos3.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos5.png | 4 +- ...ler_Rgba32_TestPattern150x150_Lanczos8.png | 4 +- ...2_TestPattern150x150_MitchellNetravali.png | 4 +- ...a32_TestPattern150x150_NearestNeighbor.png | 4 +- ...ler_Rgba32_TestPattern150x150_Robidoux.png | 4 +- ...gba32_TestPattern150x150_RobidouxSharp.png | 4 +- ...mpler_Rgba32_TestPattern150x150_Spline.png | 4 +- ...ler_Rgba32_TestPattern150x150_Triangle.png | 4 +- ...ampler_Rgba32_TestPattern150x150_Welch.png | 4 +- .../DrawImageTests/DrawTransformed.png | 4 +- ...otate_WithAngle_TestPattern100x50_-170.png | 4 +- ...Rotate_WithAngle_TestPattern100x50_-50.png | 4 +- ...Rotate_WithAngle_TestPattern100x50_170.png | 4 +- .../Rotate_WithAngle_TestPattern100x50_50.png | 4 +- ...otate_WithAngle_TestPattern50x100_-170.png | 4 +- ...Rotate_WithAngle_TestPattern50x100_-50.png | 4 +- ...Rotate_WithAngle_TestPattern50x100_170.png | 4 +- .../Rotate_WithAngle_TestPattern50x100_50.png | 4 +- ...lType_Bgra32_TestPattern100x50_-20_-10.png | 4 +- ...xelType_Bgra32_TestPattern100x50_20_10.png | 4 +- ...elType_Rgb24_TestPattern100x50_-20_-10.png | 4 +- ...ixelType_Rgb24_TestPattern100x50_20_10.png | 4 +- ...w_WorksWithAllResamplers_ducky_Bicubic.png | 4 +- .../Skew_WorksWithAllResamplers_ducky_Box.png | 4 +- ...orksWithAllResamplers_ducky_CatmullRom.png | 4 +- ...w_WorksWithAllResamplers_ducky_Hermite.png | 4 +- ..._WorksWithAllResamplers_ducky_Lanczos2.png | 4 +- ..._WorksWithAllResamplers_ducky_Lanczos3.png | 4 +- ..._WorksWithAllResamplers_ducky_Lanczos5.png | 4 +- ..._WorksWithAllResamplers_ducky_Lanczos8.png | 4 +- ...hAllResamplers_ducky_MitchellNetravali.png | 4 +- ...ithAllResamplers_ducky_NearestNeighbor.png | 4 +- ..._WorksWithAllResamplers_ducky_Robidoux.png | 4 +- ...sWithAllResamplers_ducky_RobidouxSharp.png | 4 +- ...ew_WorksWithAllResamplers_ducky_Spline.png | 4 +- ..._WorksWithAllResamplers_ducky_Triangle.png | 4 +- ...kew_WorksWithAllResamplers_ducky_Welch.png | 4 +- 65 files changed, 409 insertions(+), 198 deletions(-) diff --git a/src/ImageSharp/Processing/AffineTransformBuilder.cs b/src/ImageSharp/Processing/AffineTransformBuilder.cs index d16b2fe55..59264698b 100644 --- a/src/ImageSharp/Processing/AffineTransformBuilder.cs +++ b/src/ImageSharp/Processing/AffineTransformBuilder.cs @@ -11,7 +11,8 @@ namespace SixLabors.ImageSharp.Processing; /// public class AffineTransformBuilder { - private readonly List> matrixFactories = new List>(); + private readonly List> transformMatrixFactories = new(); + private readonly List> boundsMatrixFactories = new(); /// /// Prepends a rotation matrix using the given rotation angle in degrees @@ -29,7 +30,9 @@ public class AffineTransformBuilder /// The amount of rotation, in radians. /// The . public AffineTransformBuilder PrependRotationRadians(float radians) - => this.Prepend(size => TransformUtils.CreateRotationMatrixRadians(radians, size)); + => this.Prepend( + size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size), + size => TransformUtils.CreateRotationBoundsMatrixRadians(radians, size)); /// /// Prepends a rotation matrix using the given rotation in degrees at the given origin. @@ -65,7 +68,9 @@ public class AffineTransformBuilder /// The amount of rotation, in radians. /// The . public AffineTransformBuilder AppendRotationRadians(float radians) - => this.Append(size => TransformUtils.CreateRotationMatrixRadians(radians, size)); + => this.Append( + size => TransformUtils.CreateRotationTransformMatrixRadians(radians, size), + size => TransformUtils.CreateRotationBoundsMatrixRadians(radians, size)); /// /// Appends a rotation matrix using the given rotation in degrees at the given origin. @@ -140,7 +145,9 @@ public class AffineTransformBuilder /// The Y angle, in degrees. /// The . public AffineTransformBuilder PrependSkewDegrees(float degreesX, float degreesY) - => this.Prepend(size => TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, size)); + => this.Prepend( + size => TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, size), + size => TransformUtils.CreateSkewBoundsMatrixDegrees(degreesX, degreesY, size)); /// /// Prepends a centered skew matrix from the give angles in radians. @@ -149,7 +156,9 @@ public class AffineTransformBuilder /// The Y angle, in radians. /// The . public AffineTransformBuilder PrependSkewRadians(float radiansX, float radiansY) - => this.Prepend(size => TransformUtils.CreateSkewMatrixRadians(radiansX, radiansY, size)); + => this.Prepend( + size => TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size), + size => TransformUtils.CreateSkewBoundsMatrixRadians(radiansX, radiansY, size)); /// /// Prepends a skew matrix using the given angles in degrees at the given origin. @@ -178,7 +187,9 @@ public class AffineTransformBuilder /// The Y angle, in degrees. /// The . public AffineTransformBuilder AppendSkewDegrees(float degreesX, float degreesY) - => this.Append(size => TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, size)); + => this.Append( + size => TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, size), + size => TransformUtils.CreateSkewBoundsMatrixDegrees(degreesX, degreesY, size)); /// /// Appends a centered skew matrix from the give angles in radians. @@ -187,7 +198,9 @@ public class AffineTransformBuilder /// The Y angle, in radians. /// The . public AffineTransformBuilder AppendSkewRadians(float radiansX, float radiansY) - => this.Append(size => TransformUtils.CreateSkewMatrixRadians(radiansX, radiansY, size)); + => this.Append( + size => TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size), + size => TransformUtils.CreateSkewBoundsMatrixRadians(radiansX, radiansY, size)); /// /// Appends a skew matrix using the given angles in degrees at the given origin. @@ -254,7 +267,7 @@ public class AffineTransformBuilder public AffineTransformBuilder PrependMatrix(Matrix3x2 matrix) { CheckDegenerate(matrix); - return this.Prepend(_ => matrix); + return this.Prepend(_ => matrix, _ => matrix); } /// @@ -270,7 +283,7 @@ public class AffineTransformBuilder public AffineTransformBuilder AppendMatrix(Matrix3x2 matrix) { CheckDegenerate(matrix); - return this.Append(_ => matrix); + return this.Append(_ => matrix, _ => matrix); } /// @@ -281,7 +294,7 @@ public class AffineTransformBuilder public Matrix3x2 BuildMatrix(Size sourceSize) => this.BuildMatrix(new Rectangle(Point.Empty, sourceSize)); /// - /// Returns the combined matrix for a given source rectangle. + /// Returns the combined transform matrix for a given source rectangle. /// /// The rectangle in the source image. /// @@ -296,11 +309,11 @@ public class AffineTransformBuilder Guard.MustBeGreaterThan(sourceRectangle.Height, 0, nameof(sourceRectangle)); // Translate the origin matrix to cater for source rectangle offsets. - var matrix = Matrix3x2.CreateTranslation(-sourceRectangle.Location); + Matrix3x2 matrix = Matrix3x2.CreateTranslation(-sourceRectangle.Location); Size size = sourceRectangle.Size; - foreach (Func factory in this.matrixFactories) + foreach (Func factory in this.transformMatrixFactories) { matrix *= factory(size); } @@ -310,6 +323,32 @@ public class AffineTransformBuilder return matrix; } + /// + /// Returns the size of a rectangle large enough to contain the transformed source rectangle. + /// + /// The rectangle in the source image. + /// + /// The resultant matrix is degenerate containing one or more values equivalent + /// to or a zero determinant and therefore cannot be used + /// for linear transforms. + /// + /// The . + public Size GetTransformedSize(Rectangle sourceRectangle) + { + Size size = sourceRectangle.Size; + + // Translate the origin matrix to cater for source rectangle offsets. + Matrix3x2 matrix = Matrix3x2.CreateTranslation(-sourceRectangle.Location); + + foreach (Func factory in this.boundsMatrixFactories) + { + matrix *= factory(size); + CheckDegenerate(matrix); + } + + return TransformUtils.GetTransformedSize(size, matrix); + } + private static void CheckDegenerate(Matrix3x2 matrix) { if (TransformUtils.IsDegenerate(matrix)) @@ -318,15 +357,17 @@ public class AffineTransformBuilder } } - private AffineTransformBuilder Prepend(Func factory) + private AffineTransformBuilder Prepend(Func transformFactory, Func boundsFactory) { - this.matrixFactories.Insert(0, factory); + this.transformMatrixFactories.Insert(0, transformFactory); + this.boundsMatrixFactories.Insert(0, boundsFactory); return this; } - private AffineTransformBuilder Append(Func factory) + private AffineTransformBuilder Append(Func transformFactory, Func boundsFactory) { - this.matrixFactories.Add(factory); + this.transformMatrixFactories.Add(transformFactory); + this.boundsMatrixFactories.Add(boundsFactory); return this; } } diff --git a/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs index e88f000a7..60f90b10f 100644 --- a/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs +++ b/src/ImageSharp/Processing/Extensions/Transforms/TransformExtensions.cs @@ -51,7 +51,7 @@ public static class TransformExtensions IResampler sampler) { Matrix3x2 transform = builder.BuildMatrix(sourceRectangle); - Size targetDimensions = TransformUtils.GetTransformedSize(sourceRectangle.Size, transform); + Size targetDimensions = builder.GetTransformedSize(sourceRectangle); return source.Transform(sourceRectangle, transform, targetDimensions, sampler); } @@ -113,7 +113,7 @@ public static class TransformExtensions IResampler sampler) { Matrix4x4 transform = builder.BuildMatrix(sourceRectangle); - Size targetDimensions = TransformUtils.GetTransformedSize(sourceRectangle.Size, transform); + Size targetDimensions = builder.GetTransformedSize(sourceRectangle); return source.Transform(sourceRectangle, transform, targetDimensions, sampler); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtility.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtility.cs index c92f42429..b5eb202c1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtility.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtility.cs @@ -38,8 +38,8 @@ internal static class LinearTransformUtility /// /// The radius. /// The center position. - /// The min allowed amouunt. - /// The max allowed amouunt. + /// The min allowed amount. + /// The max allowed amount. /// The . [MethodImpl(InliningOptions.ShortMethod)] public static int GetRangeStart(float radius, float center, int min, int max) @@ -51,8 +51,8 @@ internal static class LinearTransformUtility /// /// The radius. /// The center position. - /// The min allowed amouunt. - /// The max allowed amouunt. + /// The min allowed amount. + /// The max allowed amount. /// The . [MethodImpl(InliningOptions.ShortMethod)] public static int GetRangeEnd(float radius, float center, int min, int max) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs index b8a8f424b..6580636a2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs @@ -28,14 +28,15 @@ public sealed class RotateProcessor : AffineTransformProcessor /// The source image size public RotateProcessor(float degrees, IResampler sampler, Size sourceSize) : this( - TransformUtils.CreateRotationMatrixDegrees(degrees, sourceSize), + TransformUtils.CreateRotationTransformMatrixDegrees(degrees, sourceSize), + TransformUtils.CreateRotationBoundsMatrixDegrees(degrees, sourceSize), sampler, sourceSize) => this.Degrees = degrees; // Helper constructor - private RotateProcessor(Matrix3x2 rotationMatrix, IResampler sampler, Size sourceSize) - : base(rotationMatrix, sampler, TransformUtils.GetTransformedSize(sourceSize, rotationMatrix)) + private RotateProcessor(Matrix3x2 rotationMatrix, Matrix3x2 boundsMatrix, IResampler sampler, Size sourceSize) + : base(rotationMatrix, sampler, TransformUtils.GetTransformedSize(sourceSize, boundsMatrix)) { } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs index 5f16ec530..97b18de6c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs @@ -30,7 +30,8 @@ public sealed class SkewProcessor : AffineTransformProcessor /// The source image size public SkewProcessor(float degreesX, float degreesY, IResampler sampler, Size sourceSize) : this( - TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, sourceSize), + TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, sourceSize), + TransformUtils.CreateSkewBoundsMatrixDegrees(degreesX, degreesY, sourceSize), sampler, sourceSize) { @@ -39,8 +40,8 @@ public sealed class SkewProcessor : AffineTransformProcessor } // Helper constructor: - private SkewProcessor(Matrix3x2 skewMatrix, IResampler sampler, Size sourceSize) - : base(skewMatrix, sampler, TransformUtils.GetTransformedSize(sourceSize, skewMatrix)) + private SkewProcessor(Matrix3x2 skewMatrix, Matrix3x2 boundsMatrix, IResampler sampler, Size sourceSize) + : base(skewMatrix, sampler, TransformUtils.GetTransformedSize(sourceSize, boundsMatrix)) { } diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs index 6ae0bbbdb..70112ab5a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformUtils.cs @@ -29,7 +29,7 @@ internal static class TransformUtils public static bool IsDegenerate(Matrix4x4 matrix) => IsNaN(matrix) || IsZero(matrix.GetDeterminant()); - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsZero(float a) => a > -Constants.EpsilonSquared && a < Constants.EpsilonSquared; @@ -39,13 +39,11 @@ internal static class TransformUtils /// /// The transform matrix. /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNaN(Matrix3x2 matrix) - { - return float.IsNaN(matrix.M11) || float.IsNaN(matrix.M12) - || float.IsNaN(matrix.M21) || float.IsNaN(matrix.M22) - || float.IsNaN(matrix.M31) || float.IsNaN(matrix.M32); - } + => float.IsNaN(matrix.M11) || float.IsNaN(matrix.M12) + || float.IsNaN(matrix.M21) || float.IsNaN(matrix.M22) + || float.IsNaN(matrix.M31) || float.IsNaN(matrix.M32); /// /// Returns a value that indicates whether the specified matrix contains any values @@ -53,14 +51,12 @@ internal static class TransformUtils /// /// The transform matrix. /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNaN(Matrix4x4 matrix) - { - return float.IsNaN(matrix.M11) || float.IsNaN(matrix.M12) || float.IsNaN(matrix.M13) || float.IsNaN(matrix.M14) - || float.IsNaN(matrix.M21) || float.IsNaN(matrix.M22) || float.IsNaN(matrix.M23) || float.IsNaN(matrix.M24) - || float.IsNaN(matrix.M31) || float.IsNaN(matrix.M32) || float.IsNaN(matrix.M33) || float.IsNaN(matrix.M34) - || float.IsNaN(matrix.M41) || float.IsNaN(matrix.M42) || float.IsNaN(matrix.M43) || float.IsNaN(matrix.M44); - } + => float.IsNaN(matrix.M11) || float.IsNaN(matrix.M12) || float.IsNaN(matrix.M13) || float.IsNaN(matrix.M14) + || float.IsNaN(matrix.M21) || float.IsNaN(matrix.M22) || float.IsNaN(matrix.M23) || float.IsNaN(matrix.M24) + || float.IsNaN(matrix.M31) || float.IsNaN(matrix.M32) || float.IsNaN(matrix.M33) || float.IsNaN(matrix.M34) + || float.IsNaN(matrix.M41) || float.IsNaN(matrix.M42) || float.IsNaN(matrix.M43) || float.IsNaN(matrix.M44); /// /// Applies the projective transform against the given coordinates flattened into the 2D space. @@ -69,71 +65,121 @@ internal static class TransformUtils /// The "y" vector coordinate. /// The transform matrix. /// The . - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 ProjectiveTransform2D(float x, float y, Matrix4x4 matrix) { - const float Epsilon = 0.0000001F; - var v4 = Vector4.Transform(new Vector4(x, y, 0, 1F), matrix); - return new Vector2(v4.X, v4.Y) / MathF.Max(v4.W, Epsilon); + const float epsilon = 0.0000001F; + Vector4 v4 = Vector4.Transform(new Vector4(x, y, 0, 1F), matrix); + return new Vector2(v4.X, v4.Y) / MathF.Max(v4.W, epsilon); } /// - /// Creates a centered rotation matrix using the given rotation in degrees and the source size. + /// Creates a centered rotation transform matrix using the given rotation in degrees and the source size. /// /// The amount of rotation, in degrees. /// The source image size. /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static Matrix3x2 CreateRotationMatrixDegrees(float degrees, Size size) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateRotationTransformMatrixDegrees(float degrees, Size size) => CreateCenteredTransformMatrix( new Rectangle(Point.Empty, size), Matrix3x2Extensions.CreateRotationDegrees(degrees, PointF.Empty)); /// - /// Creates a centered rotation matrix using the given rotation in radians and the source size. + /// Creates a centered rotation transform matrix using the given rotation in radians and the source size. /// /// The amount of rotation, in radians. /// The source image size. /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static Matrix3x2 CreateRotationMatrixRadians(float radians, Size size) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateRotationTransformMatrixRadians(float radians, Size size) => CreateCenteredTransformMatrix( new Rectangle(Point.Empty, size), Matrix3x2Extensions.CreateRotation(radians, PointF.Empty)); /// - /// Creates a centered skew matrix from the give angles in degrees and the source size. + /// Creates a centered rotation bounds matrix using the given rotation in degrees and the source size. + /// + /// The amount of rotation, in degrees. + /// The source image size. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateRotationBoundsMatrixDegrees(float degrees, Size size) + => CreateCenteredBoundsMatrix( + new Rectangle(Point.Empty, size), + Matrix3x2Extensions.CreateRotationDegrees(degrees, PointF.Empty)); + + /// + /// Creates a centered rotation bounds matrix using the given rotation in radians and the source size. + /// + /// The amount of rotation, in radians. + /// The source image size. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateRotationBoundsMatrixRadians(float radians, Size size) + => CreateCenteredBoundsMatrix( + new Rectangle(Point.Empty, size), + Matrix3x2Extensions.CreateRotation(radians, PointF.Empty)); + + /// + /// Creates a centered skew transform matrix from the give angles in degrees and the source size. /// /// The X angle, in degrees. /// The Y angle, in degrees. /// The source image size. /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static Matrix3x2 CreateSkewMatrixDegrees(float degreesX, float degreesY, Size size) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateSkewTransformMatrixDegrees(float degreesX, float degreesY, Size size) => CreateCenteredTransformMatrix( new Rectangle(Point.Empty, size), Matrix3x2Extensions.CreateSkewDegrees(degreesX, degreesY, PointF.Empty)); /// - /// Creates a centered skew matrix from the give angles in radians and the source size. + /// Creates a centered skew transform matrix from the give angles in radians and the source size. /// /// The X angle, in radians. /// The Y angle, in radians. /// The source image size. /// The . - [MethodImpl(InliningOptions.ShortMethod)] - public static Matrix3x2 CreateSkewMatrixRadians(float radiansX, float radiansY, Size size) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateSkewTransformMatrixRadians(float radiansX, float radiansY, Size size) => CreateCenteredTransformMatrix( new Rectangle(Point.Empty, size), Matrix3x2Extensions.CreateSkew(radiansX, radiansY, PointF.Empty)); /// - /// Gets the centered transform matrix based upon the source and destination rectangles. + /// Creates a centered skew bounds matrix from the give angles in degrees and the source size. + /// + /// The X angle, in degrees. + /// The Y angle, in degrees. + /// The source image size. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateSkewBoundsMatrixDegrees(float degreesX, float degreesY, Size size) + => CreateCenteredBoundsMatrix( + new Rectangle(Point.Empty, size), + Matrix3x2Extensions.CreateSkewDegrees(degreesX, degreesY, PointF.Empty)); + + /// + /// Creates a centered skew bounds matrix from the give angles in radians and the source size. + /// + /// The X angle, in radians. + /// The Y angle, in radians. + /// The source image size. + /// The . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateSkewBoundsMatrixRadians(float radiansX, float radiansY, Size size) + => CreateCenteredBoundsMatrix( + new Rectangle(Point.Empty, size), + Matrix3x2Extensions.CreateSkew(radiansX, radiansY, PointF.Empty)); + + /// + /// Gets the centered transform matrix based upon the source rectangle. /// /// The source image bounds. /// The transformation matrix. /// The - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Matrix3x2 CreateCenteredTransformMatrix(Rectangle sourceRectangle, Matrix3x2 matrix) { Rectangle destinationRectangle = GetTransformedBoundingRectangle(sourceRectangle, matrix); @@ -142,8 +188,33 @@ internal static class TransformUtils // This ensures scaling matrices are correct. Matrix3x2.Invert(matrix, out Matrix3x2 inverted); - var translationToTargetCenter = Matrix3x2.CreateTranslation(new Vector2(-destinationRectangle.Width, -destinationRectangle.Height) * .5F); - var translateToSourceCenter = Matrix3x2.CreateTranslation(new Vector2(sourceRectangle.Width, sourceRectangle.Height) * .5F); + // Centered transforms must be 0 based so we offset the bounds width and height. + Matrix3x2 translationToTargetCenter = Matrix3x2.CreateTranslation(new Vector2(-(destinationRectangle.Width - 1), -(destinationRectangle.Height - 1)) * .5F); + Matrix3x2 translateToSourceCenter = Matrix3x2.CreateTranslation(new Vector2(sourceRectangle.Width - 1, sourceRectangle.Height - 1) * .5F); + + // Translate back to world space. + Matrix3x2.Invert(translationToTargetCenter * inverted * translateToSourceCenter, out Matrix3x2 centered); + + return centered; + } + + /// + /// Gets the centered bounds matrix based upon the source rectangle. + /// + /// The source image bounds. + /// The transformation matrix. + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Matrix3x2 CreateCenteredBoundsMatrix(Rectangle sourceRectangle, Matrix3x2 matrix) + { + Rectangle destinationRectangle = GetTransformedBoundingRectangle(sourceRectangle, matrix); + + // We invert the matrix to handle the transformation from screen to world space. + // This ensures scaling matrices are correct. + Matrix3x2.Invert(matrix, out Matrix3x2 inverted); + + Matrix3x2 translationToTargetCenter = Matrix3x2.CreateTranslation(new Vector2(-destinationRectangle.Width, -destinationRectangle.Height) * .5F); + Matrix3x2 translateToSourceCenter = Matrix3x2.CreateTranslation(new Vector2(sourceRectangle.Width, sourceRectangle.Height) * .5F); // Translate back to world space. Matrix3x2.Invert(translationToTargetCenter * inverted * translateToSourceCenter, out Matrix3x2 centered); @@ -160,7 +231,7 @@ internal static class TransformUtils /// An enumeration that indicates on which corners to taper the rectangle. /// The amount to taper. /// The - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Matrix4x4 CreateTaperMatrix(Size size, TaperSide side, TaperCorner corner, float fraction) { Matrix4x4 matrix = Matrix4x4.Identity; @@ -281,7 +352,7 @@ internal static class TransformUtils /// /// The . /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rectangle GetTransformedBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix) { Rectangle transformed = GetTransformedRectangle(rectangle, matrix); @@ -303,10 +374,10 @@ internal static class TransformUtils return rectangle; } - var tl = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix); - var tr = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix); - var bl = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix); - var br = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix); + Vector2 tl = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix); + Vector2 tr = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix); + Vector2 bl = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix); + Vector2 br = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix); return GetBoundingRectangle(tl, tr, bl, br); } @@ -341,7 +412,7 @@ internal static class TransformUtils /// /// The . /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rectangle GetTransformedRectangle(Rectangle rectangle, Matrix4x4 matrix) { if (rectangle.Equals(default) || Matrix4x4.Identity.Equals(matrix)) @@ -365,7 +436,7 @@ internal static class TransformUtils /// /// The . /// - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Size GetTransformedSize(Size size, Matrix4x4 matrix) { Guard.IsTrue(size.Width > 0 && size.Height > 0, nameof(size), "Source size dimensions cannot be 0!"); @@ -380,7 +451,7 @@ internal static class TransformUtils return ConstrainSize(rectangle); } - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Size ConstrainSize(Rectangle rectangle) { // We want to resize the canvas here taking into account any translations. @@ -402,7 +473,7 @@ internal static class TransformUtils return new Size(width, height); } - [MethodImpl(InliningOptions.ShortMethod)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Rectangle GetBoundingRectangle(Vector2 tl, Vector2 tr, Vector2 bl, Vector2 br) { // Find the minimum and maximum "corners" based on the given vectors diff --git a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs index ad0888ad7..0387adebb 100644 --- a/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs +++ b/src/ImageSharp/Processing/ProjectiveTransformBuilder.cs @@ -11,7 +11,8 @@ namespace SixLabors.ImageSharp.Processing; /// public class ProjectiveTransformBuilder { - private readonly List> matrixFactories = new(); + private readonly List> transformMatrixFactories = new(); + private readonly List> boundsMatrixFactories = new(); /// /// Prepends a matrix that performs a tapering projective transform. @@ -21,7 +22,9 @@ public class ProjectiveTransformBuilder /// The amount to taper. /// The . public ProjectiveTransformBuilder PrependTaper(TaperSide side, TaperCorner corner, float fraction) - => this.Prepend(size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction)); + => this.Prepend( + size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction), + size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction)); /// /// Appends a matrix that performs a tapering projective transform. @@ -31,7 +34,9 @@ public class ProjectiveTransformBuilder /// The amount to taper. /// The . public ProjectiveTransformBuilder AppendTaper(TaperSide side, TaperCorner corner, float fraction) - => this.Append(size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction)); + => this.Append( + size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction), + size => TransformUtils.CreateTaperMatrix(size, side, corner, fraction)); /// /// Prepends a centered rotation matrix using the given rotation in degrees. @@ -47,7 +52,9 @@ public class ProjectiveTransformBuilder /// The amount of rotation, in radians. /// The . public ProjectiveTransformBuilder PrependRotationRadians(float radians) - => this.Prepend(size => new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, size))); + => this.Prepend( + size => new Matrix4x4(TransformUtils.CreateRotationTransformMatrixRadians(radians, size)), + size => new Matrix4x4(TransformUtils.CreateRotationBoundsMatrixRadians(radians, size))); /// /// Prepends a centered rotation matrix using the given rotation in degrees at the given origin. @@ -81,7 +88,9 @@ public class ProjectiveTransformBuilder /// The amount of rotation, in radians. /// The . public ProjectiveTransformBuilder AppendRotationRadians(float radians) - => this.Append(size => new Matrix4x4(TransformUtils.CreateRotationMatrixRadians(radians, size))); + => this.Append( + size => new Matrix4x4(TransformUtils.CreateRotationTransformMatrixRadians(radians, size)), + size => new Matrix4x4(TransformUtils.CreateRotationBoundsMatrixRadians(radians, size))); /// /// Appends a centered rotation matrix using the given rotation in degrees at the given origin. @@ -165,7 +174,9 @@ public class ProjectiveTransformBuilder /// The Y angle, in radians. /// The . public ProjectiveTransformBuilder PrependSkewRadians(float radiansX, float radiansY) - => this.Prepend(size => new Matrix4x4(TransformUtils.CreateSkewMatrixRadians(radiansX, radiansY, size))); + => this.Prepend( + size => new Matrix4x4(TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size)), + size => new Matrix4x4(TransformUtils.CreateSkewBoundsMatrixRadians(radiansX, radiansY, size))); /// /// Prepends a skew matrix using the given angles in degrees at the given origin. @@ -203,7 +214,9 @@ public class ProjectiveTransformBuilder /// The Y angle, in radians. /// The . public ProjectiveTransformBuilder AppendSkewRadians(float radiansX, float radiansY) - => this.Append(size => new Matrix4x4(TransformUtils.CreateSkewMatrixRadians(radiansX, radiansY, size))); + => this.Append( + size => new Matrix4x4(TransformUtils.CreateSkewTransformMatrixRadians(radiansX, radiansY, size)), + size => new Matrix4x4(TransformUtils.CreateSkewBoundsMatrixRadians(radiansX, radiansY, size))); /// /// Appends a skew matrix using the given angles in degrees at the given origin. @@ -270,7 +283,7 @@ public class ProjectiveTransformBuilder public ProjectiveTransformBuilder PrependMatrix(Matrix4x4 matrix) { CheckDegenerate(matrix); - return this.Prepend(_ => matrix); + return this.Prepend(_ => matrix, _ => matrix); } /// @@ -286,7 +299,7 @@ public class ProjectiveTransformBuilder public ProjectiveTransformBuilder AppendMatrix(Matrix4x4 matrix) { CheckDegenerate(matrix); - return this.Append(_ => matrix); + return this.Append(_ => matrix, _ => matrix); } /// @@ -317,7 +330,7 @@ public class ProjectiveTransformBuilder Size size = sourceRectangle.Size; - foreach (Func factory in this.matrixFactories) + foreach (Func factory in this.transformMatrixFactories) { matrix *= factory(size); } @@ -327,6 +340,32 @@ public class ProjectiveTransformBuilder return matrix; } + /// + /// Returns the size of a rectangle large enough to contain the transformed source rectangle. + /// + /// The rectangle in the source image. + /// + /// The resultant matrix is degenerate containing one or more values equivalent + /// to or a zero determinant and therefore cannot be used + /// for linear transforms. + /// + /// The . + public Size GetTransformedSize(Rectangle sourceRectangle) + { + Size size = sourceRectangle.Size; + + // Translate the origin matrix to cater for source rectangle offsets. + Matrix4x4 matrix = Matrix4x4.CreateTranslation(new Vector3(-sourceRectangle.Location, 0)); + + foreach (Func factory in this.boundsMatrixFactories) + { + matrix *= factory(size); + CheckDegenerate(matrix); + } + + return TransformUtils.GetTransformedSize(size, matrix); + } + private static void CheckDegenerate(Matrix4x4 matrix) { if (TransformUtils.IsDegenerate(matrix)) @@ -335,15 +374,17 @@ public class ProjectiveTransformBuilder } } - private ProjectiveTransformBuilder Prepend(Func factory) + private ProjectiveTransformBuilder Prepend(Func transformFactory, Func boundsFactory) { - this.matrixFactories.Insert(0, factory); + this.transformMatrixFactories.Insert(0, transformFactory); + this.boundsMatrixFactories.Insert(0, boundsFactory); return this; } - private ProjectiveTransformBuilder Append(Func factory) + private ProjectiveTransformBuilder Append(Func transformFactory, Func boundsFactory) { - this.matrixFactories.Add(factory); + this.transformMatrixFactories.Add(transformFactory); + this.boundsMatrixFactories.Add(boundsFactory); return this; } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs index 0fc5e286d..895cf60fd 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs @@ -261,6 +261,34 @@ public class AffineTransformTests image.CompareToReferenceOutput(ValidatorComparer, provider, testOutputDetails: radians); } + [Fact] + public void TransformRotationDoesNotOffset() + { + Rgba32 marker = Color.Aqua; + + using Image img = new(100, 100, Color.DimGray); + img[0, 0] = marker; + + img.Mutate(c => c.Rotate(180)); + + Assert.Equal(marker, img[99, 99]); + + using Image img2 = new(100, 100, Color.DimGray); + img2[0, 0] = marker; + + img2.Mutate( + c => + c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180), KnownResamplers.NearestNeighbor)); + + using Image img3 = new(100, 100, Color.DimGray); + img3[0, 0] = marker; + + img3.Mutate(c => c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180))); + + ImageComparer.Exact.VerifySimilarity(img, img2); + ImageComparer.Exact.VerifySimilarity(img, img3); + } + private static IResampler GetResampler(string name) { PropertyInfo property = typeof(KnownResamplers).GetTypeInfo().GetProperty(name); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs index a744a0ecc..21eda034e 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs @@ -185,6 +185,34 @@ public class ProjectiveTransformTests image.CompareToReferenceOutput(ValidatorComparer, provider, testOutputDetails: radians); } + [Fact] + public void TransformRotationDoesNotOffset() + { + Rgba32 marker = Color.Aqua; + + using Image img = new(100, 100, Color.DimGray); + img[0, 0] = marker; + + img.Mutate(c => c.Rotate(180)); + + Assert.Equal(marker, img[99, 99]); + + using Image img2 = new(100, 100, Color.DimGray); + img2[0, 0] = marker; + + img2.Mutate( + c => + c.Transform(new ProjectiveTransformBuilder().AppendRotationDegrees(180), KnownResamplers.NearestNeighbor)); + + using Image img3 = new(100, 100, Color.DimGray); + img3[0, 0] = marker; + + img3.Mutate(c => c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180))); + + ImageComparer.Exact.VerifySimilarity(img, img2); + ImageComparer.Exact.VerifySimilarity(img, img3); + } + private static IResampler GetResampler(string name) { PropertyInfo property = typeof(KnownResamplers).GetTypeInfo().GetProperty(name); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs index c17fd2379..9d256efc1 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformBuilderTestBase.cs @@ -97,7 +97,7 @@ public abstract class TransformBuilderTestBase this.AppendRotationDegrees(builder, degrees); // TODO: We should also test CreateRotationMatrixDegrees() (and all TransformUtils stuff!) for correctness - Matrix3x2 matrix = TransformUtils.CreateRotationMatrixDegrees(degrees, size); + Matrix3x2 matrix = TransformUtils.CreateRotationTransformMatrixDegrees(degrees, size); var position = new Vector2(x, y); var expected = Vector2.Transform(position, matrix); @@ -151,7 +151,7 @@ public abstract class TransformBuilderTestBase this.AppendSkewDegrees(builder, degreesX, degreesY); - Matrix3x2 matrix = TransformUtils.CreateSkewMatrixDegrees(degreesX, degreesY, size); + Matrix3x2 matrix = TransformUtils.CreateSkewTransformMatrixDegrees(degreesX, degreesY, size); var position = new Vector2(x, y); var expected = Vector2.Transform(position, matrix); diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,1)_T(-20,-10).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,1)_T(-20,-10).png index d2ee69ce8..e8edb57b9 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,1)_T(-20,-10).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,1)_T(-20,-10).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6b13fd15f767271628930510b9a00cc71c8dbe37158cdee7459e8184b2c254b -size 689 +oid sha256:8dc4da0a0c727f2c95bbfdcc7710ca612b008dca97dfc5101175c384c010641b +size 770 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,1)_T(0,0).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,1)_T(0,0).png index b3dc8be7f..20fa4e18f 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,1)_T(0,0).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,1)_T(0,0).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c186653c2431d706175ac59b06637852942d18a9177b21772d3ab511c563202 -size 723 +oid sha256:5ec2dd66678dc1a0ae3bd4bf6667fbb8ceb8a8af63ca878c490545e303c9d1ad +size 851 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,1)_T(20,10).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,1)_T(20,10).png index f00a63f90..beb117e11 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,1)_T(20,10).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,1)_T(20,10).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:291e1044a062943cc91840cd252e7cc4722d8f5bc59386597c6c2aac5565b5f4 -size 757 +oid sha256:febfdb0554e69f7fe363bca5aaff4b0a5347d216b29a0ba8cbebe92aa8678015 +size 892 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,2)_T(0,0).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,2)_T(0,0).png index 1b361c467..da8e446c0 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,2)_T(0,0).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(1,2)_T(0,0).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8f2167445c7ca069b0f33b56f4e758e9929ff5753d076c987cf53c5d7cc3bc2 -size 3318 +oid sha256:b66a5f9d8a7f3f2a78b868bec6c7d1deea927b82d81aa6d1677e0461a3920dc9 +size 3800 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(2,1)_T(0,0).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(2,1)_T(0,0).png index 7a8fe2087..4c45ba8c6 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(2,1)_T(0,0).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(0)_S(2,1)_T(0,0).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:462eaba7681f5a43f3012f8b6445180173b398c932a3cd8ec2e15cb1df9d9c4e -size 4327 +oid sha256:d5fdc46ee866e088e0ec3221145a3d2d954a0bcb6d25cbb4d538978272f34949 +size 4871 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png index 49c7795fe..2ba0560e6 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(-20,-10).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ef489dc0837b382ad7c7ead6b7c7042dfbfba39902d4cc81b5f3805d5b03967 -size 9175 +oid sha256:1b926c8335eca5530d8704739cecae0799cc651139daedb1f88ac85b0ee1bd5d +size 9484 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(0,0).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(0,0).png index ad39ebb12..d141a2e28 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(0,0).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(0,0).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d1fb97a28b5f754150343a3dc3c6974ac9abbb1577a44d88db61f0169983db0 -size 11083 +oid sha256:7f79389f79d91ac6749f21c27a592edfd2cff6efbb1d46296a26ae60d4e721f8 +size 10103 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(20,10).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(20,10).png index 20d15c665..d01fcb4a4 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(20,10).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1,1)_T(20,10).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c78c6310b6b716e9fdb1605b5b356fa7e1b0fbed9f6e6ff0d705d5152ce52665 -size 11148 +oid sha256:322c7e061f8565efdc19642e27353ec3073ee43d8c17fbef8c13be3bb60d11dc +size 10190 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.1,1.3)_T(30,-20).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.1,1.3)_T(30,-20).png index 713ce3103..171080746 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.1,1.3)_T(30,-20).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.1,1.3)_T(30,-20).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4b00b7801fd844035d9235f3d1163587e9847001b918b2d971ea7e917485371 -size 13683 +oid sha256:544e7bac188d0869f98ec075fa0e73ab831e4dafe40c1520dce194df6a53c9b8 +size 12737 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png index df59e9693..07c65142a 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50_R(50)_S(1.5,1.5)_T(0,0).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b6f27c2361cc100b6928195522e0c8e76ee3ceeda814bd036d0636957024c9f -size 22761 +oid sha256:2ccc08769974e4088702d2c95fd274af7e02095955953b424a6313d656a77735 +size 19974 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50__original.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50__original.png index a6ff73cf8..20fa4e18f 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50__original.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScaleTranslate_Rgba32_TestPattern100x50__original.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99d6c1d6b092a2feba2aebe2e09c521c3cc9682f3d748927cdc3cbaa38448b28 -size 710 +oid sha256:5ec2dd66678dc1a0ae3bd4bf6667fbb8ceb8a8af63ca878c490545e303c9d1ad +size 851 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScale_ManuallyCentered_Rgba32_TestPattern96x96_R(50)_S(0.8).png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScale_ManuallyCentered_Rgba32_TestPattern96x96_R(50)_S(0.8).png index 27a25224d..84fffa468 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScale_ManuallyCentered_Rgba32_TestPattern96x96_R(50)_S(0.8).png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_RotateScale_ManuallyCentered_Rgba32_TestPattern96x96_R(50)_S(0.8).png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad5beec2c4a09ef8c4718bb39bda5b2d0bfcccfa4634b7458ac748d2fda498fe -size 11738 +oid sha256:c1179b300b35d526bab148833ab6240f1207b8ade36674b1f47cc5a2d47a084c +size 10603 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png index a909194b0..1f1d53051 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Bicubic.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04a3b84c668758b586f4d998f080ef96b09f726b0298b28f5dd3d739b0e90744 -size 13138 +oid sha256:f666fe67ee4a1c7152fc6190affba95ea4cbd857d96bac0968e5f1fd89792d32 +size 13486 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png index 5a8c0f2fa..0ce7ad562 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Box.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21f13832d15b9491e8b73711b3081fa56c08ca69bd9b165c323dec0ba4e6f99f -size 11358 +oid sha256:5b05406a1d95f0709a7aaab7c1f57ba161b7907b76746f61788cfe527796a489 +size 4131 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png index a909194b0..e93934b48 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_CatmullRom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04a3b84c668758b586f4d998f080ef96b09f726b0298b28f5dd3d739b0e90744 -size 13138 +oid sha256:be52d36cc8f616a781c8b1416ca0bf6207b9acd580e9c06e1ee5ad434d48ab38 +size 13481 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png index 7bfe76553..2a68a381d 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Hermite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:829f7e7315a5a0cc3e88115529305ddb0c53a104863a8a66f6ad1f2efc440109 -size 12231 +oid sha256:33c99b8f0fb5d10a273a90946767f93ab6cd2dd1942f9829d695987db30dccfa +size 12488 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png index e248b6d91..08f89da07 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ea7ca66c31474c0bb9673a0d85c1c7465e387ebabf4e2d1e8f9daebfc7c8f34 -size 13956 +oid sha256:af2c0201c59065a500ae985e9b7ca164e5bcb4ce2d8d8305103398830472e07c +size 14206 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png index 5c81a5f5d..85d487103 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6dd98ac441f3c20ea999f058c7b21601d5981d46e9b77709c25f2930a64edb93 -size 17148 +oid sha256:4cef17988c4a3a667dede3dd86ed61d0507a84e5b846f52459683fd04e5a396a +size 17297 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png index 1647aae60..347cbf089 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5c4772d9b9dc57c4b5d47450ec9d02d96e40656cf2015f171b5425945f8a598 -size 18726 +oid sha256:9699a81572c03c2bc47d8bbdd1d64df26f87df3d4ad59fb6f164f6e82786d91d +size 18853 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png index 394919724..69fe0b135 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Lanczos8.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb025c4470cec1b0d6544924e46b84cbdb90d75da5d0f879f2c7d7ec9875dee2 -size 20574 +oid sha256:4fb1f59c5393debdff9bd4b7a6c222b7a0686e6d5ef24363e3d5c94ba9b5bc27 +size 20725 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png index da8413be5..0fa8cf0c0 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_MitchellNetravali.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4abaa06827cb779026f8fbb655692bdd8adab37ae5b00c3ae18ebea456eb8d9 -size 13459 +oid sha256:8ffa8ca6a60de9fe26a191edc2127886c61c072c1aa2b91fe3125512fe40e1b3 +size 13848 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png index 06c6bfa22..36e409ecb 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_NearestNeighbor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:37332e56f71506663b00deacc52adaa7574167565e395217b493d235965b29b9 -size 11563 +oid sha256:abf1d0f323795c0aaff0ff8b488d9866c5b2f7c64aad83701cb1f60e22668b0e +size 4161 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png index 5bdf26140..f8f102e4c 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Robidoux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3435ade8f7988779280820342e16881b049f717735d2218ac5a971a1bd807db1 -size 13448 +oid sha256:9b0f3c41248138bd501ae844e5b54fb9f49e5d22bab9b2ef0a0654034708b99f +size 14027 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png index 0e2dbf256..fc46cad7c 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_RobidouxSharp.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0098aa45e820544dd16a58396fa70860886b7d79900916ed97376a8328a5ff2 -size 13367 +oid sha256:6a3f839d64984b9fda4125c6643f4699add6f95373a2194c5726ed3740565a47 +size 13725 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png index 27ed945dc..58e879d4e 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Spline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7729495277b80a42e24dd8f40cdd8a280443575710fb4e199e4871c28b558271 -size 14253 +oid sha256:2ac143bc73612cecfffbec049a7b68234e7bf7581e680c3f996a977c6d671cc1 +size 14865 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png index 90c47e96d..fa25146d9 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Triangle.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2304c234b93bdabaa018263dec70e62090ad1bbb7005ef62643b88600a863fb -size 12157 +oid sha256:4eb9dab20d5a03c0adde05a9b741d9e1b0fb8c3d79054a8bc5788de496e5c7f8 +size 12420 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png index 581b22950..dc8ea690c 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_WithSampler_Rgba32_TestPattern150x150_Welch.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54b0da9646b7f4cf83df784d69dfbec48e0bdc1788d70a9872817543f72f57c1 -size 16829 +oid sha256:0f56ee78cc2fd698ac8ea84912648f0b49d4b4d66b439f6976447c56a44c2998 +size 16909 diff --git a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png index c04521ebc..17238cf2f 100644 --- a/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png +++ b/tests/Images/External/ReferenceOutput/Drawing/DrawImageTests/DrawTransformed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:233d8c6c9e101dddf5d210d47c7a20807f8f956738289068ea03b774258ef8c6 -size 182754 +oid sha256:00836a98742d177a2376af32d8d858fcf9f26a4da5a311dd5faf5cd80f233c0b +size 184397 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-170.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-170.png index e5ac34ff9..a9289abd0 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-170.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-170.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:964e8e5c160d7cd57e3b1fc584a39c1c8c61b835ff6af8264ec0631175286fca -size 10794 +oid sha256:801067dfb19b2a9a1fbd14b771e780b475c21f3ccdc1e709bbc20d62061ad1d1 +size 8782 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-50.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-50.png index 5b2ecebd6..43fcd5df5 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-50.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2997d51c4ca534df2561f2c3bf1801f04631277b4259e25d9ef3fc164212f722 -size 11223 +oid sha256:ccbdd3dcdbc888923e85495fbd7837478cca813be6ecece63ee645bdf39d436f +size 10325 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_170.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_170.png index d620913d4..9a7f9866d 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_170.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_170.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8bd1219a9363bcc8dc088fb0a0a17c5e1914d418c89f4affc3fb9abf236f705 -size 10725 +oid sha256:60d1be2ffa6d50f97561b92efe8d0c0337f9c121582e38c9ab9af75be8eed32d +size 8539 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_50.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_50.png index ad39ebb12..d141a2e28 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_50.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern100x50_50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d1fb97a28b5f754150343a3dc3c6974ac9abbb1577a44d88db61f0169983db0 -size 11083 +oid sha256:7f79389f79d91ac6749f21c27a592edfd2cff6efbb1d46296a26ae60d4e721f8 +size 10103 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-170.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-170.png index 8b15fa6c4..1d27e2395 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-170.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-170.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e62ccc2cca36898a01445ef03361b84ff19aa1c0498e9dcbf21703dc1c009dd -size 11278 +oid sha256:75fb59ea2947efb1abf73cd824e54b6e8f6271343bd2fdadb005b29476988921 +size 9423 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-50.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-50.png index 8bb5f272b..628d0c889 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-50.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_-50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f8f1f14a3c32d8d7e0406c2cf9480aa78dcef7bf223966e80f620680ef68375c -size 12064 +oid sha256:9c2d94791af40e001fc44b23eeecac7a606492c747b22ede0cdc7069ef121cb8 +size 11193 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_170.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_170.png index 6245b3226..0e927cfbd 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_170.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_170.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7756d2402f4cf7d221d832d921fcff80fef6a36da5373cd9cb46b8e0f00e2fb1 -size 11273 +oid sha256:016de6e82b6fb03fd55168ea7fc12ab245d0e0387ca7c32d3ef1158a85a8facd +size 9330 diff --git a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_50.png b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_50.png index 28435befb..ac4d47362 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_50.png +++ b/tests/Images/External/ReferenceOutput/Transforms/RotateTests/Rotate_WithAngle_TestPattern50x100_50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d49ac2a6529959eb2a5d257c804ba576f9426fafe918c7fb267fc397477666dd -size 12051 +oid sha256:9219cc118fe7195b730cbe2e6407cde54e6f4c7930a71b7418bc7c273aa4120c +size 11050 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_-20_-10.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_-20_-10.png index 163cc401d..147f9c989 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_-20_-10.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_-20_-10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d56651460aa5c942d800f2c92f0934f4c53b0f83e6fe8a2eb891a85bd9d93542 -size 10244 +oid sha256:2b243340f372220033b349a96cbd7ecd732395fa15e4d1ed62048d2031c42794 +size 8398 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_20_10.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_20_10.png index d080ba778..d1252cb2c 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_20_10.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Bgra32_TestPattern100x50_20_10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2912a3cd8088e9ad5e4e33b7c85825aedf9f910c32abbe78f3560510024b770 -size 10141 +oid sha256:fdd2745aba2f09bb4a09e881339fe62774ce5658902aa9d83b3a1e0718260084 +size 8694 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_-20_-10.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_-20_-10.png index 058b229a2..235e95d8f 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_-20_-10.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_-20_-10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58aea9e0fb398d84dc2f27e1b06ad53d195f2dd06bb1f489c6e51fae5724867d -size 7478 +oid sha256:281bd00550ab1cf9b05011750e6de330cdd42a8644ecf3b7c176bd5c6e94c59b +size 6098 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_20_10.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_20_10.png index 899864e53..3b63f58f3 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_20_10.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_IsNotBoundToSinglePixelType_Rgb24_TestPattern100x50_20_10.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49b3eaeedfdd1c90b5ec48bf07e387d6cac8062ba15826971c22868b29ecb769 -size 7462 +oid sha256:e755ed61d7eab2c297f4b6592366b54ac801cbdb3d920741dfdd04dfaf73f9b9 +size 6086 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Bicubic.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Bicubic.png index 0e0106041..340455428 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Bicubic.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Bicubic.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e27f9c12e743d3bcc328dc7e5708c738c5a8ccba3ac99a465bb2fbc045afdc45 -size 28062 +oid sha256:8e8afa56c5abb0e4b5895f35415db1178d041120d9f8306902f554cfaaada88d +size 26540 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Box.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Box.png index 8f9dd4e74..9ef786692 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Box.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Box.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3dfbc7ca2c42fba08daff44f29fdc64f76cb5515ee8f0fd5798270ed64fdeb27 -size 26282 +oid sha256:a2c174ef54b68f025352e25800f655fe3b94a0d3f75cb48bd2ac0e8d6931faf8 +size 24827 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_CatmullRom.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_CatmullRom.png index 65e094dc1..14f774853 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_CatmullRom.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_CatmullRom.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad0e7cf115b2057fa223c45340f67b1659be6f6acefad32e0b02ca35327ffdf8 -size 28052 +oid sha256:6b56ceae2f350a1402beecc5b5e2930be1011a95fbf224cccf73b96f3931b646 +size 26531 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Hermite.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Hermite.png index 6d1eb77e1..c8204eacf 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Hermite.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Hermite.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ca5e0729371222626d711a3f425c602198f5b51c8629e59027e37d63fadf5fa -size 25870 +oid sha256:049ee7fc2bb758609a64149c338bfae2eab44755f53e6b7c25a5e8b8725ed8ac +size 24416 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos2.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos2.png index 9098e51be..2bc57092a 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos2.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf1df2d5b53bc779f01e6ad32ca502cd5fb40826cb1fbf5ddc55b769f68e0d8c -size 28060 +oid sha256:72c487a2fa3d608021b26a4d6b4517f8548fdcfc62fbafdd8649015dbec8ff87 +size 26504 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos3.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos3.png index 00f4e8d92..fee364e21 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos3.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f21b8a574beb978befbb44e65def5a4d818fbca6be211cccc455fd819c278531 -size 34325 +oid sha256:099733c9d4490c86cfbb10a016e2dd073539a95f9d5ff9018cf9b5be5404fa13 +size 33435 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos5.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos5.png index 69afe9a6f..30325ccc6 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos5.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ced1fc39a97381af2ef369b94351a96930a58052343dcaa464b12da1747906f -size 37066 +oid sha256:27f2a2b21f8ae878e15120ea5a4a983bde7984b3468dc8426055885efc278fe6 +size 35547 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos8.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos8.png index 53e0dfa07..ff81256a7 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos8.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Lanczos8.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b60fa32cf39ee70103d8498e5dc751fe2d5801fa30ff9f0948376fac64cb1021 -size 41427 +oid sha256:6b5cbe60e26e123e5a5cdf5a4e88305340f76d32a9c64a220c1fa7512f84e786 +size 39442 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_MitchellNetravali.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_MitchellNetravali.png index 9216e2f17..263dd7426 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_MitchellNetravali.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_MitchellNetravali.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce56cc11a369838e011a796cc380f9f06ca915a845e5b6b0425cb7366f162a5c -size 27303 +oid sha256:102cceb79acb1dfd7ec8887b4906e33456c774d48320d1624b3c73975d26f145 +size 25981 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_NearestNeighbor.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_NearestNeighbor.png index 8f9dd4e74..9ef786692 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_NearestNeighbor.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_NearestNeighbor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3dfbc7ca2c42fba08daff44f29fdc64f76cb5515ee8f0fd5798270ed64fdeb27 -size 26282 +oid sha256:a2c174ef54b68f025352e25800f655fe3b94a0d3f75cb48bd2ac0e8d6931faf8 +size 24827 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Robidoux.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Robidoux.png index 79ef7ef28..85bbd5ec3 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Robidoux.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Robidoux.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ce82b69d9dde2621a3d54647e7a659d440e2f9b0102831773a97abca4bcfa4c -size 27248 +oid sha256:e61629aeefac7e0a1a6b46b44ad86ed4a5ba0908bb3febc18bb5f9f3ded1c08d +size 25751 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_RobidouxSharp.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_RobidouxSharp.png index 823f471f2..f200a5f95 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_RobidouxSharp.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_RobidouxSharp.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71d45938ec05003c5dcae0962ac6847041410123ba1dc2debbbf41e22ac2d91a -size 27519 +oid sha256:45b1b48e1df393f4c435189c71a6bd3bccfe7a055d76d414a8d0c009b59fa0a0 +size 26145 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Spline.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Spline.png index 23766e557..434bb32a8 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Spline.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Spline.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:75bd6138967d8795d0be7fe2e76c889718276924d702349105003c24f08957e2 -size 25559 +oid sha256:d6d186f9e547f658b719bc033e3b110d64cf2a02caecc510d4e2f88359e69746 +size 24176 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Triangle.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Triangle.png index 33e7b6ef1..e3be1ffe5 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Triangle.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Triangle.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b280a880b864a14225fd433378dede02cfad897059939cebbe0e04659de6d5a9 -size 25382 +oid sha256:339b3299984f1450f1a8200e487964c0338b511b82e459d67a3583d0bd46b805 +size 24013 diff --git a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Welch.png b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Welch.png index a170cc2cb..7dbeeaf35 100644 --- a/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Welch.png +++ b/tests/Images/External/ReferenceOutput/Transforms/SkewTests/Skew_WorksWithAllResamplers_ducky_Welch.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c94d6ae8ad8ddab17a5ad1cfc73e711912a3f13e6bd3dfe9e5f0c8ec2c004af -size 33257 +oid sha256:5335c6184829fdc405475bd34d2fae60cf6d5ae050b4d671ac5dd25242ff1368 +size 31888 From ac5ace789c09f60fd598521bf678f5eb18ac40df Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 3 Jul 2024 21:39:16 +1000 Subject: [PATCH 79/79] Update tests --- .../Processors/Transforms/AffineTransformTests.cs | 9 +++++---- .../Processing/Transforms/ProjectiveTransformTests.cs | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs index d37d4760f..ab97dfbf6 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs @@ -252,23 +252,24 @@ public class AffineTransformTests [Fact] public void TransformRotationDoesNotOffset() { - Rgba32 marker = Color.Aqua; + Rgba32 background = Color.DimGray.ToPixel(); + Rgba32 marker = Color.Aqua.ToPixel(); - using Image img = new(100, 100, Color.DimGray); + using Image img = new(100, 100, background); img[0, 0] = marker; img.Mutate(c => c.Rotate(180)); Assert.Equal(marker, img[99, 99]); - using Image img2 = new(100, 100, Color.DimGray); + using Image img2 = new(100, 100, background); img2[0, 0] = marker; img2.Mutate( c => c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180), KnownResamplers.NearestNeighbor)); - using Image img3 = new(100, 100, Color.DimGray); + using Image img3 = new(100, 100, background); img3[0, 0] = marker; img3.Mutate(c => c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180))); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs index 21eda034e..4aec53036 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs @@ -188,23 +188,24 @@ public class ProjectiveTransformTests [Fact] public void TransformRotationDoesNotOffset() { - Rgba32 marker = Color.Aqua; + Rgba32 background = Color.DimGray.ToPixel(); + Rgba32 marker = Color.Aqua.ToPixel(); - using Image img = new(100, 100, Color.DimGray); + using Image img = new(100, 100, background); img[0, 0] = marker; img.Mutate(c => c.Rotate(180)); Assert.Equal(marker, img[99, 99]); - using Image img2 = new(100, 100, Color.DimGray); + using Image img2 = new(100, 100, background); img2[0, 0] = marker; img2.Mutate( c => c.Transform(new ProjectiveTransformBuilder().AppendRotationDegrees(180), KnownResamplers.NearestNeighbor)); - using Image img3 = new(100, 100, Color.DimGray); + using Image img3 = new(100, 100, background); img3[0, 0] = marker; img3.Mutate(c => c.Transform(new AffineTransformBuilder().AppendRotationDegrees(180)));