HSL.cs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. using Microsoft.Xna.Framework;
  2. using System;
  3. namespace OpenVIII
  4. {
  5. /// <summary>
  6. /// </summary>
  7. /// <seealso cref="https://medium.com/@donatbalipapp/colours-maths-90346fb5abda"/>
  8. /// <see cref="https://www.rapidtables.com/convert/color/rgb-to-hsl.html"/>
  9. /// <seealso cref="https://en.wikipedia.org/wiki/HSL_and_HSV"/>
  10. public struct HSL
  11. {
  12. #region Fields
  13. /// <summary>
  14. /// Alpha
  15. /// </summary>
  16. /// <remarks>Percent</remarks>
  17. public float A;
  18. /// <summary>
  19. /// Hue
  20. /// </summary>
  21. /// <remarks>Degrees</remarks>
  22. public float H;
  23. /// <summary>
  24. /// Luminosity
  25. /// </summary>
  26. /// <remarks>Percent</remarks>
  27. public float L;
  28. /// <summary>
  29. /// Saturation
  30. /// </summary>
  31. /// <remarks>Percent</remarks>
  32. public float S;
  33. #endregion Fields
  34. #region Constructors
  35. public HSL(Color @in)
  36. {
  37. var cMax = @in.Max() / 255f;
  38. var cMin = @in.Min() / 255f;
  39. var delta = cMax - cMin;
  40. float R = @in.R / 255f, G = @in.G / 255f, B = @in.B / 255f;
  41. H = Hue();
  42. var l = L = Luminosity();
  43. S = Saturation();
  44. A = @in.A / 255f;
  45. float Hue()
  46. {
  47. var ret = 0f;
  48. if (delta == 0)
  49. { }
  50. else if (cMax == R)
  51. {
  52. ret = 60f * (((G - B) / delta) % 6);
  53. }
  54. else if (cMax == G)
  55. {
  56. ret = 60f * (((B - R) / delta) + 2);
  57. }
  58. else if (cMax == B)
  59. {
  60. ret = 60f * (((R - G) / delta) + 4);
  61. }
  62. return ret;
  63. }
  64. float Luminosity() => (cMax + cMin) / 2;
  65. float Saturation() => delta == 0 || l == 1 ? 0f : (delta) / (1 - Math.Abs(l * 2 - 1));
  66. }
  67. #endregion Constructors
  68. #region Methods
  69. /// <summary>
  70. /// HSL to Color
  71. /// </summary>
  72. /// <see cref="https://www.rapidtables.com/convert/color/hsl-to-rgb.html"/>
  73. public static implicit operator Color(HSL @in)
  74. {
  75. var C = (1 - Math.Abs(2 * @in.L - 1)) * @in.S;
  76. var H = @in.H / 60f;
  77. var X = C * (1 - Math.Abs(H % 2 - 1));
  78. var m = @in.L - C / 2;
  79. var rgb = getRGB();
  80. return new Color()
  81. {
  82. A = (byte)(@in.A * 255),
  83. R = (byte)Math.Round(rgb.X),
  84. G = (byte)Math.Round(rgb.Y),
  85. B = (byte)Math.Round(rgb.Z),
  86. };
  87. Vector3 getPrime()
  88. {
  89. if (H >= 0)
  90. {
  91. if (H <= 1)
  92. return new Vector3(C, X, 0);
  93. else if (H <= 2)
  94. return new Vector3(X, C, 0);
  95. else if (H <= 3)
  96. return new Vector3(0, C, X);
  97. else if (H <= 4)
  98. return new Vector3(0, X, C);
  99. else if (H <= 5)
  100. return new Vector3(X, 0, C);
  101. else if (H <= 6)
  102. return new Vector3(C, 0, X);
  103. }
  104. return Vector3.Zero;
  105. }
  106. Vector3 getRGB()
  107. {
  108. var prime = getPrime();
  109. prime += new Vector3(m);
  110. prime *= new Vector3(255);
  111. return prime;
  112. }
  113. }
  114. public static implicit operator HSL(Color @in) => new HSL(@in);
  115. public override string ToString() => $"{H}° {S * 100}% {L * 100}% {A * 100}%";
  116. #endregion Methods
  117. }
  118. public static class ColorExt
  119. {
  120. #region Methods
  121. public static HSL HSL(this Color @in) => new HSL(@in);
  122. public static byte Max(this Color @in) => (byte)MathHelper.Clamp((MathHelper.Max(MathHelper.Max(@in.R, @in.G), @in.B)), 0, 255);
  123. public static byte Min(this Color @in) => (byte)MathHelper.Clamp((MathHelper.Min(MathHelper.Min(@in.R, @in.G), @in.B)), 0, 255);
  124. #endregion Methods
  125. }
  126. }