StandardColors.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. using System.Collections.Frozen;
  2. using System.Collections.Immutable;
  3. using System.Diagnostics.CodeAnalysis;
  4. namespace Terminal.Gui.Drawing;
  5. /// <summary>
  6. /// Helper class for transforming to and from <see cref="StandardColor"/> enum.
  7. /// </summary>
  8. internal static class StandardColors
  9. {
  10. // Lazy initialization to avoid static constructor convoy effect in parallel scenarios
  11. private static readonly Lazy<ImmutableArray<string>> _names = new (
  12. NamesValueFactory,
  13. LazyThreadSafetyMode.PublicationOnly);
  14. private static ImmutableArray<string> NamesValueFactory ()
  15. {
  16. string [] standardNames = Enum.GetNames<StandardColor> ().Order ().ToArray ();
  17. return [.. standardNames];
  18. }
  19. private static readonly Lazy<FrozenDictionary<uint, string>> _argbNameMap = new (
  20. MapValueFactory,
  21. LazyThreadSafetyMode.PublicationOnly);
  22. private static FrozenDictionary<uint, string> MapValueFactory ()
  23. {
  24. string [] standardNames = Enum.GetNames<StandardColor> ()
  25. .Order ()
  26. .ToArray ();
  27. Dictionary<uint, string> map = new (standardNames.Length);
  28. foreach (string name in standardNames)
  29. {
  30. var standardColor = Enum.Parse<StandardColor> (name);
  31. uint argb = GetArgb (standardColor);
  32. // TODO: Collect aliases?
  33. _ = map.TryAdd (argb, name);
  34. }
  35. return map.ToFrozenDictionary ();
  36. }
  37. /// <summary>
  38. /// Gets read-only list of the W3C colors in alphabetical order.
  39. /// </summary>
  40. public static IReadOnlyList<string> GetColorNames () => _names.Value;
  41. /// <summary>
  42. /// Converts the given Standard (W3C+) color name to equivalent color value.
  43. /// </summary>
  44. /// <param name="name">Standard (W3C+) color name.</param>
  45. /// <param name="color">The successfully converted Standard (W3C+) color value.</param>
  46. /// <returns>True if the conversion succeeded; otherwise false.</returns>
  47. public static bool TryParseColor (ReadOnlySpan<char> name, out Color color)
  48. {
  49. if (!Enum.TryParse (name, true, out StandardColor standardColor)
  50. ||
  51. // Any numerical value converts to undefined enum value.
  52. !Enum.IsDefined (standardColor))
  53. {
  54. color = default (Color);
  55. return false;
  56. }
  57. uint argb = GetArgb (standardColor);
  58. color = new (argb);
  59. return true;
  60. }
  61. /// <summary>
  62. /// Converts the given color value to a Standard (W3C+) color name.
  63. /// </summary>
  64. /// <param name="color">Color value to match Standard (W3C+)color.</param>
  65. /// <param name="name">The successfully converted Standard (W3C+) color name.</param>
  66. /// <returns>True if conversion succeeded; otherwise false.</returns>
  67. public static bool TryNameColor (Color color, [NotNullWhen (true)] out string? name)
  68. {
  69. // Ignore alpha channel when matching - alpha represents transparency, not color identity
  70. uint opaqueArgb = color.Argb | 0xFF000000;
  71. if (_argbNameMap.Value.TryGetValue (opaqueArgb, out name))
  72. {
  73. return true;
  74. }
  75. name = null;
  76. return false;
  77. }
  78. internal static uint GetArgb (StandardColor standardColor)
  79. {
  80. const int ALPHA_SHIFT = 24;
  81. const uint ALPHA_MASK = 0xFFU << ALPHA_SHIFT;
  82. var rgb = (int)standardColor;
  83. uint argb = (uint)rgb | ALPHA_MASK;
  84. return argb;
  85. }
  86. }