📷 A modern, cross-platform, 2D Graphics library for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

156 lines
5.0 KiB

// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.ColorSpaces
{
/// <summary>
/// Represents a Hsl (hue, saturation, lightness) color.
/// </summary>
internal readonly struct Hsl : IColorVector, IEquatable<Hsl>, IAlmostEquatable<Hsl, float>
{
/// <summary>
/// Max range used for clamping.
/// </summary>
private static readonly Vector3 VectorMax = new Vector3(360, 1, 1);
/// <summary>
/// The backing vector for SIMD support.
/// </summary>
private readonly Vector3 backingVector;
/// <summary>
/// Initializes a new instance of the <see cref="Hsl"/> struct.
/// </summary>
/// <param name="h">The h hue component.</param>
/// <param name="s">The s saturation component.</param>
/// <param name="l">The l value (lightness) component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hsl(float h, float s, float l)
: this(new Vector3(h, s, l))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Hsl"/> struct.
/// </summary>
/// <param name="vector">The vector representing the h, s, l components.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Hsl(Vector3 vector)
{
this.backingVector = Vector3.Clamp(vector, Vector3.Zero, VectorMax);
}
/// <summary>
/// Gets the hue component.
/// <remarks>A value ranging between 0 and 360.</remarks>
/// </summary>
public float H
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.X;
}
/// <summary>
/// Gets the saturation component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float S
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Y;
}
/// <summary>
/// Gets the lightness component.
/// <remarks>A value ranging between 0 and 1.</remarks>
/// </summary>
public float L
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.backingVector.Z;
}
/// <inheritdoc/>
public Vector3 Vector => this.backingVector;
/// <summary>
/// Compares two <see cref="Hsl"/> objects for equality.
/// </summary>
/// <param name="left">
/// The <see cref="Hsl"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Hsl"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is equal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Hsl left, Hsl right)
{
return left.Equals(right);
}
/// <summary>
/// Compares two <see cref="Hsl"/> objects for inequality.
/// </summary>
/// <param name="left">
/// The <see cref="Hsl"/> on the left side of the operand.
/// </param>
/// <param name="right">
/// The <see cref="Hsl"/> on the right side of the operand.
/// </param>
/// <returns>
/// True if the current left is unequal to the <paramref name="right"/> parameter; otherwise, false.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Hsl left, Hsl right)
{
return !left.Equals(right);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
return this.backingVector.GetHashCode();
}
/// <inheritdoc/>
public override string ToString()
{
return this.Equals(default)
? "Hsl [ Empty ]"
: $"Hsl [ H={this.H:#0.##}, S={this.S:#0.##}, L={this.L:#0.##} ]";
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return obj is Hsl other && this.Equals(other);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Hsl other)
{
return this.backingVector.Equals(other.backingVector);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AlmostEquals(Hsl other, float precision)
{
var result = Vector3.Abs(this.backingVector - other.backingVector);
return result.X <= precision
&& result.Y <= precision
&& result.Z <= precision;
}
}
}