// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace SixLabors.Fonts { /// /// Provides access to the color details for the current glyph. /// public readonly partial struct GlyphColor : IEquatable { internal GlyphColor(byte red, byte green, byte blue, byte alpha) { this.R = red; this.G = green; this.B = blue; this.A = alpha; } /// /// Gets the red component /// public readonly byte R { get; } /// /// Gets the green component /// public readonly byte G { get; } /// /// Gets the blue component /// public readonly byte B { get; } /// /// Gets the alpha component /// public readonly byte A { 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 ==(GlyphColor left, GlyphColor 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 !=(GlyphColor left, GlyphColor right) => !left.Equals(right); /// public override bool Equals(object? obj) => obj is GlyphColor p && this.Equals(p); /// /// Compares the for equality to this color. /// /// /// The other to compare to. /// /// /// True if the current color is equal to the parameter; otherwise, false. /// public bool Equals(GlyphColor other) => other.R == this.R && other.G == this.G && other.B == this.B && other.A == this.A; /// public override int GetHashCode() => HashCode.Combine( this.R, this.G, this.B, this.A); /// /// Gets the hexadecimal string representation of the color instance in the format RRGGBBAA. /// /// /// The hexadecimal representation of the combined color components. /// /// /// When this method returns, contains the equivalent of the hexadecimal input. /// /// /// if the parsing was successful; otherwise, . /// public static bool TryParseHex(string? value, [NotNullWhen(true)] out GlyphColor result) { result = default; if (string.IsNullOrWhiteSpace(value)) { return false; } ReadOnlySpan hex = value.AsSpan(); if (hex[0] != '#') { return false; } hex = hex[1..]; byte a = 255, r, g, b; switch (hex.Length) { case 8: if (!TryParseByte(hex[0], hex[1], out r) || !TryParseByte(hex[2], hex[3], out g) || !TryParseByte(hex[4], hex[5], out b) || !TryParseByte(hex[6], hex[7], out a)) { return false; } break; case 6: if (!TryParseByte(hex[0], hex[1], out r) || !TryParseByte(hex[2], hex[3], out g) || !TryParseByte(hex[4], hex[5], out b)) { return false; } break; case 4: if (!TryExpand(hex[0], out r) || !TryExpand(hex[1], out g) || !TryExpand(hex[2], out b) || !TryExpand(hex[3], out a)) { return false; } break; case 3: if (!TryExpand(hex[0], out r) || !TryExpand(hex[1], out g) || !TryExpand(hex[2], out b)) { return false; } break; default: return false; } result = new GlyphColor(r, g, b, a); return true; } /// /// Attempts to parse the specified name into a corresponding named glyph color. /// /// The name of the glyph color to parse. /// /// When this method returns, contains the parsed value if the parse operation succeeded; /// otherwise, contains the default value. /// /// /// if the parsing was successful; otherwise, . /// public static bool TryParseNamed(string? name, [NotNullWhen(true)] out GlyphColor result) { result = default; if (string.IsNullOrWhiteSpace(name)) { return false; } return NamedGlyphColorsLookupLazy.Value.TryGetValue(name, out result); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryParseByte(char hi, char lo, out byte value) { if (TryConvertHexCharToByte(hi, out byte high) && TryConvertHexCharToByte(lo, out byte low)) { value = (byte)((high << 4) | low); return true; } value = 0; return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryExpand(char c, out byte value) { if (TryConvertHexCharToByte(c, out byte nibble)) { value = (byte)((nibble << 4) | nibble); return true; } value = 0; return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryConvertHexCharToByte(char c, out byte value) { if ((uint)(c - '0') <= 9) { value = (byte)(c - '0'); return true; } char lower = (char)(c | 0x20); // Normalize to lowercase if ((uint)(lower - 'a') <= 5) { value = (byte)(lower - 'a' + 10); return true; } value = 0; return false; } } }