#nullable enable
using System.Collections.Frozen;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text.Json.Serialization;
namespace Terminal.Gui;
/// Represents a 24-bit color encoded in ARGB32 format.
///
///
///
///
///
[JsonConverter (typeof (ColorJsonConverter))]
[StructLayout (LayoutKind.Explicit)]
public readonly partial record struct Color : ISpanParsable, IUtf8SpanParsable, ISpanFormattable, IUtf8SpanFormattable, IMinMaxValue {
/// The value of the alpha channel component
///
/// The alpha channel is not currently supported, so the value of the alpha channel bits will not affect rendering.
///
[JsonIgnore]
[field: FieldOffset (3)]
public readonly byte A;
///
/// The value of this as a in ARGB32 format.
///
///
/// The alpha channel is not currently supported, so the value of the alpha channel bits will not affect rendering.
///
[JsonIgnore]
[field: FieldOffset (0)]
public readonly uint Argb;
/// The value of the blue color component.
[JsonIgnore]
[field: FieldOffset (0)]
public readonly byte B;
/// The value of the green color component.
[JsonIgnore]
[field: FieldOffset (1)]
public readonly byte G;
/// The value of the red color component.
[JsonIgnore]
[field: FieldOffset (2)]
public readonly byte R;
///
/// The value of this encoded as a signed 32-bit integer in ARGB32 format.
///
[JsonIgnore]
[field: FieldOffset (0)]
public readonly int Rgba;
///
/// Initializes a new instance of the using the supplied component values.
///
/// The red 8-bits.
/// The green 8-bits.
/// The blue 8-bits.
/// Optional; defaults to 0xFF. The Alpha channel is not supported by Terminal.Gui.
/// Alpha channel is not currently supported by Terminal.Gui.
/// If the value of any parameter is greater than .
/// If the value of any parameter is negative.
public Color (int red = 0, int green = 0, int blue = 0, int alpha = byte.MaxValue)
{
ArgumentOutOfRangeException.ThrowIfNegative (red, nameof (red));
ArgumentOutOfRangeException.ThrowIfNegative (green, nameof (green));
ArgumentOutOfRangeException.ThrowIfNegative (blue, nameof (blue));
ArgumentOutOfRangeException.ThrowIfNegative (alpha, nameof (alpha));
A = Convert.ToByte (alpha);
R = Convert.ToByte (red);
G = Convert.ToByte (green);
B = Convert.ToByte (blue);
}
///
/// Initializes a new instance of the class with an encoded signed 32-bit color value in ARGB32 format.
///
/// The encoded 32-bit color value (see ).
///
/// The alpha channel is not currently supported, so the value of the alpha channel bits will not affect rendering.
///
public Color (int rgba)
{
Rgba = rgba;
}
///
/// Initializes a new instance of the class with an encoded unsigned 32-bit color value in ARGB32 format.
///
/// The encoded unsigned 32-bit color value (see ).
///
/// The alpha channel is not currently supported, so the value of the alpha channel bits will not affect rendering.
///
public Color (uint argb)
{
Argb = argb;
}
///
/// Initializes a new instance of the color from a legacy 16-color named value.
///
/// The 16-color value.
public Color (in ColorName colorName)
{
this = ColorExtensions.ColorNameToColorMap [colorName];
}
///
/// Initializes a new instance of the color from string. See for details.
///
///
/// If is .
///
/// If is an empty string or consists of only whitespace characters.
///
/// If thrown by
public Color (string colorString)
{
ArgumentException.ThrowIfNullOrWhiteSpace (colorString, nameof (colorString));
this = Parse (colorString, CultureInfo.InvariantCulture);
}
///
/// Initializes a new instance of the with all channels set to 0.
///
public Color ()
{
Argb = 0u;
}
///
/// Gets or sets the 3-byte/6-character hexadecimal value for each of the legacy 16-color values.
///
[SerializableConfigurationProperty (Scope = typeof (SettingsScope), OmitClassName = true)]
public static Dictionary Colors {
get =>
// Transform _colorToNameMap into a Dictionary
ColorExtensions.ColorToNameMap.ToDictionary (static kvp => kvp.Value, static kvp => kvp.Key.ToString ("g"));
set {
// Transform Dictionary into _colorToNameMap
ColorExtensions.ColorToNameMap = value.ToFrozenDictionary (GetColorToNameMapKey, GetColorToNameMapValue);
return;
static Color GetColorToNameMapKey (KeyValuePair kvp) => new Color (kvp.Value);
static ColorName GetColorToNameMapValue (KeyValuePair kvp) => Enum.TryParse (kvp.Key.ToString (), true, out var colorName) ? colorName : throw new ArgumentException ($"Invalid color name: {kvp.Key}");
}
}
///
/// Gets the using a legacy 16-color value. will return the closest 16
/// color match to the true color when no exact value is found.
///
///
/// Get returns the of the closest 24-bit color value. Set sets the RGB value using a hard-coded map.
///
public AnsiColorCode GetAnsiColorCode () => ColorExtensions.ColorNameToAnsiColorMap [GetClosestNamedColor ()];
///
/// Gets the using a legacy 16-color value. will return the closest
/// 16 color match to the true color when no exact value is found.
///
///
/// Get returns the of the closest 24-bit color value. Set sets the RGB value using a hard-coded map.
///
public ColorName GetClosestNamedColor () => GetClosestNamedColor (this);
///
/// Determines if the closest named to is the provided .
///
///
/// The to check if this is closer to than any other configured named color.
///
///
/// if the closest named color is the provided value.
///
/// if any other named color is closer to this than .
///
///
/// If is equidistant from two named colors, the result of this method is not guaranteed to be determinate.
///
[Pure]
[MethodImpl (MethodImplOptions.AggressiveInlining)]
public bool IsClosestToNamedColor (in ColorName namedColor) => GetClosestNamedColor () == namedColor;
///
/// Determines if the closest named to /> is the provided .
///
///
/// The color to test against the value in .
///
///
/// The to check if this is closer to than any other configured named color.
///
///
/// if the closest named color to is the provided value.
///
/// if any other named color is closer to than .
///
///
/// If is equidistant from two named colors, the result of this method is not guaranteed to be determinate.
///
[Pure]
[MethodImpl (MethodImplOptions.AggressiveInlining)]
public static bool IsColorClosestToNamedColor (in Color color, in ColorName namedColor)
{
return color.IsClosestToNamedColor (in namedColor);
}
/// Gets the "closest" named color to this value.
///
///
/// Distance is defined here as the Euclidean distance between each color interpreted as a .
///
/// The order of the values in the passed Vector3 must be
///
///
[SkipLocalsInit]
internal static ColorName GetClosestNamedColor (Color inputColor) => ColorExtensions.ColorToNameMap.MinBy (pair => CalculateColorDistance (inputColor, pair.Key)).Value;
[SkipLocalsInit]
static float CalculateColorDistance (in Vector4 color1, in Vector4 color2) => Vector4.Distance (color1, color2);
#region Legacy Color Names
/// The black color.
public const ColorName Black = ColorName.Black;
/// The blue color.
public const ColorName Blue = ColorName.Blue;
/// The green color.
public const ColorName Green = ColorName.Green;
/// The cyan color.
public const ColorName Cyan = ColorName.Cyan;
/// The red color.
public const ColorName Red = ColorName.Red;
/// The magenta color.
public const ColorName Magenta = ColorName.Magenta;
/// The yellow color.
public const ColorName Yellow = ColorName.Yellow;
/// The gray color.
public const ColorName Gray = ColorName.Gray;
/// The dark gray color.
public const ColorName DarkGray = ColorName.DarkGray;
/// The bright bBlue color.
public const ColorName BrightBlue = ColorName.BrightBlue;
/// The bright green color.
public const ColorName BrightGreen = ColorName.BrightGreen;
/// The bright cyan color.
public const ColorName BrightCyan = ColorName.BrightCyan;
/// The bright red color.
public const ColorName BrightRed = ColorName.BrightRed;
/// The bright magenta color.
public const ColorName BrightMagenta = ColorName.BrightMagenta;
/// The bright yellow color.
public const ColorName BrightYellow = ColorName.BrightYellow;
/// The White color.
public const ColorName White = ColorName.White;
#endregion
}