Create a (Discord)Color using a string (#2979)

* Create a (Discord)Color using a string

* You made me learn bitwise operators and shifting I am angery now, also AI sucks at this shit

* xmldoc

* Apply suggestions from code review

---------

Co-authored-by: Mihail Gribkov <61027276+Misha-133@users.noreply.github.com>
This commit is contained in:
Adriaan Waem
2024-08-14 21:57:25 +02:00
committed by GitHub
parent 47f98bc01d
commit bc84b94bd9
3 changed files with 129 additions and 0 deletions

View File

@@ -200,6 +200,94 @@ namespace Discord
public static implicit operator uint(Color color)
=> color.RawValue;
/// <summary>
/// Converts the string representation of a color to a Color object.
/// </summary>
/// <param name="rawValue">String to be parsed to a color</param>
/// <param name="colorType">Color format of the string</param>
/// <returns>A Color object with a color value parsed from a string</returns>
/// <exception cref="ArgumentOutOfRangeException">The argument is not a number or of incorrect length</exception>
public static Color Parse(string rawValue, ColorType colorType = ColorType.CssHexColor)
{
if (TryParse(rawValue, out var color, colorType))
return color;
throw new ArgumentOutOfRangeException(nameof(rawValue), "Value must be a number of valid length - matching the specified ColorType");
}
/// <summary>
/// Converts the string representation of a color to a Color object. The return value indicates whether the conversion succeeded.
/// </summary>
/// <param name="rawValue">String to be parsed to a color</param>
/// <param name="color">When this method returns true, contains a Color that represents the parsed string.</param>
/// <param name="colorType">Color format of the string</param>
/// <returns>true if the conversion succeeded; false otherwise.</returns>
public static bool TryParse(string rawValue, out Color color, ColorType colorType = ColorType.CssHexColor)
{
color = new Color();
if (string.IsNullOrWhiteSpace(rawValue))
return false;
rawValue = rawValue.TrimStart('#');
if (rawValue.StartsWith("0x"))
rawValue = rawValue.Substring(2);
if (!uint.TryParse(rawValue, System.Globalization.NumberStyles.HexNumber, null, out var parsedValue))
return false;
uint r;
uint g;
uint b;
uint fullHex;
switch (rawValue.Length, colorType)
{
case (3, ColorType.CssHexColor):
r = parsedValue >> 8;
g = (parsedValue & 0xF0) >> 4;
b = parsedValue & 0xF;
r = (r << 4) | r;
g = (g << 4) | g;
b = (b << 4) | b;
fullHex = (r << 16) | (g << 8) | b;
break;
case (4, ColorType.CssHexColor):
r = (parsedValue & 0xF00) >> 8;
g = (parsedValue & 0xF0) >> 4;
b = parsedValue & 0xF;
r = (r << 4) | r;
g = (g << 4) | g;
b = (b << 4) | b;
fullHex = (r << 16) | (g << 8) | b;
break;
case (6, ColorType.CssHexColor):
color = new Color(parsedValue);
return true;
case (8, ColorType.CssHexColor):
parsedValue &= 0xFFFFFF;
color = new Color(parsedValue);
return true;
default:
return false;
}
color = new Color(fullHex);
return true;
}
public override bool Equals(object obj)
=> obj is Color c && RawValue == c.RawValue;

View File

@@ -0,0 +1,21 @@
using System;
namespace Discord;
public enum ColorType
{
/// <summary>
/// Color in one of the following formats.
/// <para>
/// #RGB The three-value syntax
/// </para>
/// <para>
/// #RGBA The four-value syntax
/// </para>
/// <para>
/// #RRGGBB The six-value syntax
/// </para>
/// #RRGGBBAA The eight-value syntax
/// </summary>
CssHexColor = 0
}

View File

@@ -74,6 +74,26 @@ namespace Discord
Assert.Throws<ArgumentOutOfRangeException>(() => new Color(2.0f, 2.0f, 2.0f));
}
[Fact]
public void Color_FromRgb_String_CssHexColor()
{
Assert.Equal(0xFF0000u, Color.Parse("#F00", ColorType.CssHexColor).RawValue);
Assert.Equal(0x22BB44u, Color.Parse("#2B4", ColorType.CssHexColor).RawValue);
Assert.Equal(0xAABBAAu, Color.Parse("FABA", ColorType.CssHexColor).RawValue);
Assert.Equal(0x00F672u, Color.Parse("00F672", ColorType.CssHexColor).RawValue);
Assert.Equal(0x257777u, Color.Parse("0xFF257777", ColorType.CssHexColor).RawValue);
}
[Fact]
public void Color_FromRgb_String_Invalid()
{
Assert.Throws<ArgumentOutOfRangeException>(() => Color.Parse(" ", ColorType.CssHexColor));
Assert.Throws<ArgumentOutOfRangeException>(() => Color.Parse(null, ColorType.CssHexColor));
Assert.Throws<ArgumentOutOfRangeException>(() => Color.Parse("#F"));
Assert.Throws<ArgumentOutOfRangeException>(() => Color.Parse("F0", ColorType.CssHexColor));
Assert.Throws<ArgumentOutOfRangeException>(() => Color.Parse("FF000"));
Assert.Throws<ArgumentOutOfRangeException>(() => Color.Parse("FF00000"));
Assert.Throws<ArgumentOutOfRangeException>(() => Color.Parse("FF0000000"));
}
[Fact]
public void Color_Red()
{
Assert.Equal(0xAF, new Color(0xAF1390).R);