mirror of https://github.com/SixLabors/ImageSharp
46 changed files with 703 additions and 287 deletions
@ -0,0 +1,62 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Numerics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.ColorProfiles; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides standard YCbCr matrices for RGB to YCbCr conversion.
|
||||
|
/// </summary>
|
||||
|
public static class KnownYCbCrMatrices |
||||
|
{ |
||||
|
#pragma warning disable SA1137 // Elements should have the same indentation
|
||||
|
#pragma warning disable SA1117 // Parameters should be on same line or separate lines
|
||||
|
/// <summary>
|
||||
|
/// ITU-R BT.601 (SD video standard).
|
||||
|
/// </summary>
|
||||
|
public static readonly YCbCrMatrix BT601 = new( |
||||
|
new Matrix4x4( |
||||
|
0.299000F, 0.587000F, 0.114000F, 0F, |
||||
|
-0.168736F, -0.331264F, 0.500000F, 0F, |
||||
|
0.500000F, -0.418688F, -0.081312F, 0F, |
||||
|
0F, 0F, 0F, 1F), |
||||
|
new Matrix4x4( |
||||
|
1.000000F, 0.000000F, 1.402000F, 0F, |
||||
|
1.000000F, -0.344136F, -0.714136F, 0F, |
||||
|
1.000000F, 1.772000F, 0.000000F, 0F, |
||||
|
0F, 0F, 0F, 1F), |
||||
|
new Vector3(0F, 0.5F, 0.5F)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// ITU-R BT.709 (HD video, sRGB standard).
|
||||
|
/// </summary>
|
||||
|
public static readonly YCbCrMatrix BT709 = new( |
||||
|
new Matrix4x4( |
||||
|
0.212600F, 0.715200F, 0.072200F, 0F, |
||||
|
-0.114572F, -0.385428F, 0.500000F, 0F, |
||||
|
0.500000F, -0.454153F, -0.045847F, 0F, |
||||
|
0F, 0F, 0F, 1F), |
||||
|
new Matrix4x4( |
||||
|
1.000000F, 0.000000F, 1.574800F, 0F, |
||||
|
1.000000F, -0.187324F, -0.468124F, 0F, |
||||
|
1.000000F, 1.855600F, 0.000000F, 0F, |
||||
|
0F, 0F, 0F, 1F), |
||||
|
new Vector3(0F, 0.5F, 0.5F)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// ITU-R BT.2020 (UHD/4K video standard).
|
||||
|
/// </summary>
|
||||
|
public static readonly YCbCrMatrix BT2020 = new( |
||||
|
new Matrix4x4( |
||||
|
0.262700F, 0.678000F, 0.059300F, 0F, |
||||
|
-0.139630F, -0.360370F, 0.500000F, 0F, |
||||
|
0.500000F, -0.459786F, -0.040214F, 0F, |
||||
|
0F, 0F, 0F, 1F), |
||||
|
new Matrix4x4( |
||||
|
1.000000F, 0.000000F, 1.474600F, 0F, |
||||
|
1.000000F, -0.164553F, -0.571353F, 0F, |
||||
|
1.000000F, 1.881400F, 0.000000F, 0F, |
||||
|
0F, 0F, 0F, 1F), |
||||
|
new Vector3(0F, 0.5F, 0.5F)); |
||||
|
} |
||||
@ -1,27 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Numerics; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.ColorProfiles; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides standard Y (luma) coefficient sets for weighted RGB conversions.
|
|
||||
/// </summary>
|
|
||||
public static class KnownYCoefficients |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// ITU-R BT.601 (SD video standard).
|
|
||||
/// </summary>
|
|
||||
public static readonly Vector3 BT601 = new(0.299F, 0.587F, 0.114F); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ITU-R BT.709 (HD video, sRGB standard).
|
|
||||
/// </summary>
|
|
||||
public static readonly Vector3 BT709 = new(0.2126F, 0.7152F, 0.0722F); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// ITU-R BT.2020 (UHD/4K video standard).
|
|
||||
/// </summary>
|
|
||||
public static readonly Vector3 BT2020 = new(0.2627F, 0.6780F, 0.0593F); |
|
||||
} |
|
||||
@ -0,0 +1,58 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Numerics; |
||||
|
using SixLabors.ImageSharp.ColorProfiles.WorkingSpaces; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.ColorProfiles; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// <para>
|
||||
|
/// Represents a YCbCr color matrix containing forward and inverse transformation matrices,
|
||||
|
/// and the chrominance offsets to apply for full-range encoding
|
||||
|
/// </para>
|
||||
|
/// <para>
|
||||
|
/// These matrices must be selected to match the characteristics of the associated <see cref="RgbWorkingSpace"/>,
|
||||
|
/// including its transfer function (gamma or companding) and chromaticity coordinates. Using mismatched matrices and
|
||||
|
/// working spaces will produce incorrect conversions.
|
||||
|
/// </para>
|
||||
|
/// </summary>
|
||||
|
public readonly struct YCbCrMatrix |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="YCbCrMatrix"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="forward">
|
||||
|
/// The forward transformation matrix from RGB to YCbCr. The matrix must include the
|
||||
|
/// standard chrominance offsets in the fourth column, such as <c>(0, 0.5, 0.5)</c>.
|
||||
|
/// </param>
|
||||
|
/// <param name="inverse">
|
||||
|
/// The inverse transformation matrix from YCbCr to RGB. This matrix expects that
|
||||
|
/// chrominance offsets have already been subtracted prior to application.
|
||||
|
/// </param>
|
||||
|
/// <param name="offset">
|
||||
|
/// The chrominance offsets to be added after the forward conversion,
|
||||
|
/// and subtracted before the inverse conversion. Usually <c>(0, 0.5, 0.5)</c>.
|
||||
|
/// </param>
|
||||
|
public YCbCrMatrix(Matrix4x4 forward, Matrix4x4 inverse, Vector3 offset) |
||||
|
{ |
||||
|
this.Forward = forward; |
||||
|
this.Inverse = inverse; |
||||
|
this.Offset = offset; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the matrix used to convert gamma-encoded RGB to YCbCr.
|
||||
|
/// </summary>
|
||||
|
public Matrix4x4 Forward { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the matrix used to convert YCbCr back to gamma-encoded RGB.
|
||||
|
/// </summary>
|
||||
|
public Matrix4x4 Inverse { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the chrominance offset vector to apply during encoding (add) or decoding (subtract).
|
||||
|
/// </summary>
|
||||
|
public Vector3 Offset { get; } |
||||
|
} |
||||
@ -0,0 +1,216 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.ColorProfiles; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Represents a YCCK (luminance, blue chroma, red chroma, black) color.
|
||||
|
/// YCCK is not a true color space but a reversible transform of CMYK, where the CMY components
|
||||
|
/// are converted to YCbCr using the ITU-R BT.601 standard, and the K (black) component is preserved separately.
|
||||
|
/// </summary>
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
public readonly struct YccK : IColorProfile<YccK, Rgb> |
||||
|
{ |
||||
|
private static readonly Vector4 Min = Vector4.Zero; |
||||
|
private static readonly Vector4 Max = Vector4.One; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="YccK"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="y">The y luminance component.</param>
|
||||
|
/// <param name="cb">The cb chroma component.</param>
|
||||
|
/// <param name="cr">The cr chroma component.</param>
|
||||
|
/// <param name="k">The keyline black component.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public YccK(float y, float cb, float cr, float k) |
||||
|
: this(new Vector4(y, cb, cr, k)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="YccK"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="vector">The vector representing the c, m, y, k components.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public YccK(Vector4 vector) |
||||
|
{ |
||||
|
vector = Vector4.Clamp(vector, Min, Max); |
||||
|
this.Y = vector.X; |
||||
|
this.Cb = vector.Y; |
||||
|
this.Cr = vector.Z; |
||||
|
this.K = vector.W; |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
|
||||
|
private YccK(Vector4 vector, bool _) |
||||
|
#pragma warning restore SA1313 // Parameter names should begin with lower-case letter
|
||||
|
{ |
||||
|
this.Y = vector.X; |
||||
|
this.Cb = vector.Y; |
||||
|
this.Cr = vector.Z; |
||||
|
this.K = vector.W; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the Y luminance component.
|
||||
|
/// <remarks>A value ranging between 0 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float Y { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the C (blue) chroma component.
|
||||
|
/// <remarks>A value ranging between 0 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float Cb { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the C (red) chroma component.
|
||||
|
/// <remarks>A value ranging between 0 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float Cr { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the keyline black color component.
|
||||
|
/// <remarks>A value ranging between 0 and 1.</remarks>
|
||||
|
/// </summary>
|
||||
|
public float K { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="YccK"/> objects for equality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">The <see cref="YccK"/> on the left side of the operand.</param>
|
||||
|
/// <param name="right">The <see cref="YccK"/> on the right side of the operand.</param>
|
||||
|
/// <returns>
|
||||
|
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
|
||||
|
/// </returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator ==(YccK left, YccK right) => left.Equals(right); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares two <see cref="YccK"/> objects for inequality.
|
||||
|
/// </summary>
|
||||
|
/// <param name="left">The <see cref="YccK"/> on the left side of the operand.</param>
|
||||
|
/// <param name="right">The <see cref="YccK"/> on the right side of the operand.</param>
|
||||
|
/// <returns>
|
||||
|
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
|
||||
|
/// </returns>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public static bool operator !=(YccK left, YccK right) => !left.Equals(right); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public Vector4 ToScaledVector4() |
||||
|
{ |
||||
|
Vector4 v4 = default; |
||||
|
v4 += this.AsVector4Unsafe(); |
||||
|
return v4; |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public static YccK FromScaledVector4(Vector4 source) |
||||
|
=> new(source, true); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public static void ToScaledVector4(ReadOnlySpan<YccK> source, Span<Vector4> destination) |
||||
|
{ |
||||
|
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); |
||||
|
MemoryMarshal.Cast<YccK, Vector4>(source).CopyTo(destination); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public static void FromScaledVector4(ReadOnlySpan<Vector4> source, Span<YccK> destination) |
||||
|
{ |
||||
|
Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination)); |
||||
|
MemoryMarshal.Cast<Vector4, YccK>(source).CopyTo(destination); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public Rgb ToProfileConnectingSpace(ColorConversionOptions options) |
||||
|
{ |
||||
|
Matrix4x4 m = options.YCbCrMatrix.Inverse; |
||||
|
Vector3 offset = options.YCbCrMatrix.Offset; |
||||
|
Vector3 normalized = this.AsVector3Unsafe() - offset; |
||||
|
|
||||
|
float r = Vector3.Dot(normalized, new Vector3(m.M11, m.M12, m.M13)); |
||||
|
float g = Vector3.Dot(normalized, new Vector3(m.M21, m.M22, m.M23)); |
||||
|
float b = Vector3.Dot(normalized, new Vector3(m.M31, m.M32, m.M33)); |
||||
|
|
||||
|
Vector3 rgb = new Vector3(r, g, b) * (1F - this.K); |
||||
|
return Rgb.FromScaledVector3(rgb); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public static YccK FromProfileConnectingSpace(ColorConversionOptions options, in Rgb source) |
||||
|
{ |
||||
|
Matrix4x4 m = options.YCbCrMatrix.Forward; |
||||
|
Vector3 offset = options.YCbCrMatrix.Offset; |
||||
|
|
||||
|
Vector3 rgb = source.AsVector3Unsafe(); |
||||
|
float k = 1F - MathF.Max(rgb.X, MathF.Max(rgb.Y, rgb.Z)); |
||||
|
|
||||
|
if (k >= 1F - Constants.Epsilon) |
||||
|
{ |
||||
|
return new YccK(new Vector4(0F, 0.5F, 0.5F, 1F), true); |
||||
|
} |
||||
|
|
||||
|
rgb /= 1F - k; |
||||
|
|
||||
|
float y = Vector3.Dot(rgb, new Vector3(m.M11, m.M12, m.M13)); |
||||
|
float cb = Vector3.Dot(rgb, new Vector3(m.M21, m.M22, m.M23)); |
||||
|
float cr = Vector3.Dot(rgb, new Vector3(m.M31, m.M32, m.M33)); |
||||
|
|
||||
|
return new YccK(new Vector4(y, cb, cr, k) + new Vector4(offset, 0F)); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public static void ToProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan<YccK> source, Span<Rgb> destination) |
||||
|
{ |
||||
|
// TODO: We can possibly optimize this by using SIMD
|
||||
|
for (int i = 0; i < source.Length; i++) |
||||
|
{ |
||||
|
destination[i] = source[i].ToProfileConnectingSpace(options); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public static void FromProfileConnectionSpace(ColorConversionOptions options, ReadOnlySpan<Rgb> source, Span<YccK> 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); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public static ChromaticAdaptionWhitePointSource GetChromaticAdaptionWhitePointSource() |
||||
|
=> ChromaticAdaptionWhitePointSource.RgbWorkingSpace; |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public override int GetHashCode() |
||||
|
=> HashCode.Combine(this.Y, this.Cb, this.Cr, this.K); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override string ToString() |
||||
|
=> FormattableString.Invariant($"YccK({this.Y:#0.##}, {this.Cb:#0.##}, {this.Cr:#0.##}, {this.K:#0.##})"); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override bool Equals(object? obj) |
||||
|
=> obj is YccK other && this.Equals(other); |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public bool Equals(YccK other) |
||||
|
=> this.AsVector4Unsafe() == other.AsVector4Unsafe(); |
||||
|
|
||||
|
private Vector3 AsVector3Unsafe() => Unsafe.As<YccK, Vector3>(ref Unsafe.AsRef(in this)); |
||||
|
|
||||
|
private Vector4 AsVector4Unsafe() => Unsafe.As<YccK, Vector4>(ref Unsafe.AsRef(in this)); |
||||
|
} |
||||
@ -0,0 +1,80 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.ColorProfiles; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.ColorProfiles; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Tests <see cref="Rgb"/>-<see cref="YccK"/> conversions.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// Test data generated mathematically
|
||||
|
/// </remarks>
|
||||
|
public class RgbAndYccKConversionTests |
||||
|
{ |
||||
|
private static readonly ApproximateColorProfileComparer Comparer = new(.001F); |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(1, .5F, .5F, 0, 1, 1, 1)] |
||||
|
[InlineData(0, .5F, .5F, 1, 0, 0, 0)] |
||||
|
[InlineData(.5F, .5F, .5F, 0, .5F, .5F, .5F)] |
||||
|
public void Convert_YccK_To_Rgb(float y, float cb, float cr, float k, float r, float g, float b) |
||||
|
{ |
||||
|
// Arrange
|
||||
|
YccK input = new(y, cb, cr, k); |
||||
|
Rgb expected = new(r, g, b); |
||||
|
ColorProfileConverter converter = new(); |
||||
|
|
||||
|
Span<YccK> inputSpan = new YccK[5]; |
||||
|
inputSpan.Fill(input); |
||||
|
|
||||
|
Span<Rgb> actualSpan = new Rgb[5]; |
||||
|
|
||||
|
// Act
|
||||
|
Rgb actual = converter.Convert<YccK, Rgb>(input); |
||||
|
converter.Convert<YccK, Rgb>(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, 1, .5F, .5F, 0)] |
||||
|
[InlineData(0, 0, 0, 0, .5F, .5F, 1)] |
||||
|
[InlineData(.5F, .5F, .5F, 1, .5F, .5F, .5F)] |
||||
|
public void Convert_Rgb_To_YccK(float r, float g, float b, float y, float cb, float cr, float k) |
||||
|
{ |
||||
|
// Multiple YccK representations can decode to the same RGB value.
|
||||
|
// For example, (Y=1.0, Cb=0.5, Cr=0.5, K=0.5) and (Y=0.5, Cb=0.5, Cr=0.5, K=0.0) both yield RGB (0.5, 0.5, 0.5).
|
||||
|
// This is expected because YccK is not a unique encoding — K modulates RGB after YCbCr decoding.
|
||||
|
// Round-tripping RGB -> YccK -> RGB is stable, but YccK -> RGB -> YccK is not injective.
|
||||
|
|
||||
|
// Arrange
|
||||
|
Rgb input = new(r, g, b); |
||||
|
YccK expected = new(y, cb, cr, k); |
||||
|
ColorProfileConverter converter = new(); |
||||
|
|
||||
|
Span<Rgb> inputSpan = new Rgb[5]; |
||||
|
inputSpan.Fill(input); |
||||
|
|
||||
|
Span<YccK> actualSpan = new YccK[5]; |
||||
|
|
||||
|
// Act
|
||||
|
YccK actual = converter.Convert<Rgb, YccK>(input); |
||||
|
converter.Convert<Rgb, YccK>(inputSpan, actualSpan); |
||||
|
|
||||
|
// Assert
|
||||
|
Assert.Equal(expected, actual, Comparer); |
||||
|
|
||||
|
for (int i = 0; i < actualSpan.Length; i++) |
||||
|
{ |
||||
|
Assert.Equal(expected, actualSpan[i], Comparer); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,44 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.ColorProfiles; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.ColorProfiles; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Tests the <see cref="YccK"/> struct.
|
||||
|
/// </summary>
|
||||
|
[Trait("Color", "Conversion")] |
||||
|
public class YccKTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void YccKConstructorAssignsFields() |
||||
|
{ |
||||
|
const float y = .75F; |
||||
|
const float cb = .5F; |
||||
|
const float cr = .25F; |
||||
|
const float k = .125F; |
||||
|
|
||||
|
YccK ycckValue = new(y, cb, cr, k); |
||||
|
Assert.Equal(y, ycckValue.Y); |
||||
|
Assert.Equal(cb, ycckValue.Cb); |
||||
|
Assert.Equal(cr, ycckValue.Cr); |
||||
|
Assert.Equal(k, ycckValue.K); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void YccKEquality() |
||||
|
{ |
||||
|
YccK x = default; |
||||
|
YccK y = new(1F, 1F, 1F, 1F); |
||||
|
Assert.True(default == default(YccK)); |
||||
|
Assert.False(default != default(YccK)); |
||||
|
Assert.Equal(default, default(YccK)); |
||||
|
Assert.Equal(new YccK(1, 1, 1, 1), new YccK(1, 1, 1, 1)); |
||||
|
Assert.Equal(new YccK(.5F, .5F, .5F, .5F), new YccK(.5F, .5F, .5F, .5F)); |
||||
|
|
||||
|
Assert.False(x.Equals(y)); |
||||
|
Assert.False(x.Equals((object)y)); |
||||
|
Assert.False(x.GetHashCode().Equals(y.GetHashCode())); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue