Skip to content

Commit

Permalink
[Color] Improve parsing
Browse files Browse the repository at this point in the history
This commit improves parsing colors from strings:

  - Adds support for parsing hexadecimal color strings
  - Adds TryParse that does not throw exceptions on failure
  • Loading branch information
hyazinthh committed Oct 18, 2023
1 parent 05021b8 commit 62889e7
Show file tree
Hide file tree
Showing 4 changed files with 1,266 additions and 179 deletions.
93 changes: 93 additions & 0 deletions src/Aardvark.Base/Math/Colors/Color.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;

namespace Aardvark.Base
{
Expand Down Expand Up @@ -1900,6 +1902,97 @@ public static C4f Average(this IEnumerable<C4f> items)
}

#endregion

#region Hex Parsing

// See: https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color
private static readonly Regex regexHexSgl = new Regex("^(?:#|0x)?(?<R>[0-9a-fA-F])(?<G>[0-9a-fA-F])(?<B>[0-9a-fA-F])(?<A>[0-9a-fA-F])?$", RegexOptions.Compiled);
private static readonly Regex regexHexDbl = new Regex("^(?:#|0x)?(?<R>[0-9a-fA-F]{2})(?<G>[0-9a-fA-F]{2})(?<B>[0-9a-fA-F]{2})(?<A>[0-9a-fA-F]{2})?$", RegexOptions.Compiled);

private static bool TryParseHex(Regex regex, string input, out C4b result)
{
var m = regex.Match(input);
if (m.Success)
{
string[] values = new string[4];
values[0] = m.Groups["R"].Value;
values[1] = m.Groups["G"].Value;
values[2] = m.Groups["B"].Value;
values[3] = m.Groups["A"].Success ? m.Groups["A"].Value : "FF";

result =
new C4b(values.Map((x) => {
var value = x.Length == 1 ? new string(x[0], 2) : x;
return byte.Parse(value, NumberStyles.AllowHexSpecifier);
}));

return true;
}

result = C4b.Zero;
return false;
}

/// <summary>
/// Parses a hexadecimal color string with format RRGGBBAA or RGBA, where the alpha component is optional.
/// </summary>
/// <remarks>
/// For the single digit RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF").
/// The color string may be prefixed by "#" or "0x".
/// </remarks>
/// <param name="input">The string to be parsed.</param>
/// <param name="result">Contains the parsed color on success, C4b.Zero otherwise.</param>
/// <returns>True on success, false otherwise.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryParseHex(Text input, out C4b result)
{
var str = input.WhiteSpaceTrimmed.ToString();
return TryParseHex(regexHexDbl, str, out result) || TryParseHex(regexHexSgl, str, out result);
}

/// <summary>
/// Parses a hexadecimal color string with format RRGGBBAA or RGBA, where the alpha component is optional.
/// </summary>
/// <remarks>
/// For the single digit RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF").
/// The color string may be prefixed by "#" or "0x".
/// </remarks>
/// <param name="input">The string to be parsed.</param>
/// <param name="result">Contains the parsed color on success, C4b.Zero otherwise.</param>
/// <returns>True on success, false otherwise.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryParseHex(string input, out C4b result)
=> TryParseHex(new Text(input), out result);

/// <summary>
/// Parses a hexadecimal color string with format RRGGBBAA or RGBA, where the alpha component is optional.
/// </summary>
/// <remarks>
/// For the single digit RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF").
/// The color string may be prefixed by "#" or "0x".
/// </remarks>
/// <param name="input">The string to be parsed.</param>
/// <returns>The parsed color.</returns>
/// <exception cref="FormatException">the input does not represent a valid hexadecimal color.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static C4b ParseHex(Text input)
=> TryParseHex(input, out C4b result) ? result : throw new FormatException($"{input} is not a valid hexadecimal color.");

/// <summary>
/// Parses a hexadecimal color string with format RRGGBBAA or RGBA, where the alpha component is optional.
/// </summary>
/// <remarks>
/// For the single digit RGBA format, the components are duplicated (e.g. "F" is interpreted as "FF").
/// The color string may be prefixed by "#" or "0x".
/// </remarks>
/// <param name="input">The string to be parsed.</param>
/// <returns>The parsed color.</returns>
/// <exception cref="FormatException">the input does not represent a valid hexadecimal color.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static C4b ParseHex(string input)
=> ParseHex(new Text(input));

#endregion
}

#endregion
Expand Down
Loading

0 comments on commit 62889e7

Please sign in to comment.