diff --git a/src/Uno.UI/UI/ColorHelper.cs b/src/Uno.UI/UI/ColorHelper.cs index 26c8e53f18f8..c2cec4bf8a6f 100644 --- a/src/Uno.UI/UI/ColorHelper.cs +++ b/src/Uno.UI/UI/ColorHelper.cs @@ -73,4 +73,347 @@ public static WindowsColor FromARGB(byte a, byte r, byte g, byte b) /// The generated Color value. public static WindowsColor FromArgb(byte a, byte r, byte g, byte b) => WindowsColor.FromArgb(a, r, g, b); + + #region Resource IDs Lookup Tables + private static ReadOnlySpan HueLimitsForSatLevel4 => + [ + 0, + 11, + 26, + 0, + 0, + 38, + 45, + 0, + 0, + 56, + 100, + 121, + 129, + 0, + 140, + 0, + 180, + 0, + 0, + 224, + 241, + 0, + 256 + ]; + + private static ReadOnlySpan HueLimitsForSatLevel5 => + [ + 0, + 13, + 27, + 0, + 0, + 36, + 45, + 0, + 0, + 59, + 118, + 0, + 127, + 136, + 142, + 0, + 185, + 0, + 0, + 216, + 239, + 0, + 256 + ]; + + private static ReadOnlySpan HueLimitsForSatLevel3 => + [ + 0, + 8, + 0, + 0, + 39, + 46, + 0, + 0, + 0, + 71, + 120, + 0, + 131, + 144, + 0, + 0, + 163, + 0, + 177, + 211, + 249, + 0, + 256 + ]; + + private static ReadOnlySpan HueLimitsForSatLevel2 => + [ + 0, + 10, + 0, + 32, + 46, + 0, + 0, + 0, + 61, + 0, + 106, + 0, + 136, + 144, + 0, + 0, + 0, + 158, + 166, + 241, + 0, + 0, + 256 + ]; + + private static ReadOnlySpan HueLimitsForSatLevel1 => + [ + 8, + 0, + 0, + 44, + 0, + 0, + 0, + 63, + 0, + 0, + 122, + 0, + 134, + 0, + 0, + 0, + 0, + 166, + 176, + 241, + 0, + 256, + 0 + ]; + + private static ReadOnlySpan LumLimitsForHueIndexHigh => + [ + 170, + 170, + 170, + 155, + 170, + 170, + 170, + 170, + 170, + 115, + 170, + 170, + 170, + 170, + 170, + 170, + 170, + 170, + 150, + 150, + 170, + 140, + 165 + ]; + + private static ReadOnlySpan LumLimitsForHueIndexLow => + [ + 130, + 100, + 115, + 100, + 100, + 100, + 110, + 75, + 100, + 90, + 100, + 100, + 100, + 100, + 80, + 100, + 100, + 100, + 100, + 100, + 100, + 100, + 100 + ]; + + private static ReadOnlySpan ColorNamesMid => + [ + 5119, + 5135, + 5136, + 5137, + 5122, + 5138, + 5139, + 5140, + 5140, + 5141, + 5141, + 5142, + 5143, + 5126, + 5144, + 5129, + 5145, + 5146, + 5147, + 5148, + 5134, + 5137, + 5135 + ]; + + private static ReadOnlySpan ColorNamesDark => + [ + 5137, + 5149, + 5137, + 5137, + 5137, + 5150, + 5150, + 5137, + 5151, + 5151, + 5151, + 5151, + 5152, + 5152, + 5152, + 5153, + 5153, + 5146, + 5147, + 5154, + 5155, + 5137, + 5149 + ]; + + private static ReadOnlySpan ColorNamesLight => + [ + 5119, + 5120, + 5121, + 5122, + 5122, + 5123, + 5123, + 5122, + 5124, + 5125, + 5124, + 5124, + 5126, + 5127, + 5128, + 5129, + 5130, + 5131, + 5132, + 5133, + 5134, + 5122, + 5120 + ]; + #endregion + + private unsafe static uint GetColorNameResourceId(WindowsColor color) + { + var hsl = color.ToHsl(); + double h = hsl.H * 255.0; + double s = hsl.S * 255.0; + double l = hsl.L * 255.0; + + if (l > 240.0) + { + return 5114; + } + + if (l < 20.0) + { + return 5118; + } + + if (s > 20.0) + { + ReadOnlySpan hueLimits; + + if (s > 240.0) + { + hueLimits = HueLimitsForSatLevel5; + } + else if (s > 150.0) + { + hueLimits = HueLimitsForSatLevel4; + } + else if (s > 115.0) + { + hueLimits = HueLimitsForSatLevel3; + } + else if (s > 75.0) + { + hueLimits = HueLimitsForSatLevel2; + } + else + { + hueLimits = HueLimitsForSatLevel1; + } + + var hueIndex = 0; + while (hueIndex < 23 && hueLimits[hueIndex] <= h) + { + hueIndex++; + } + + if (l > LumLimitsForHueIndexHigh[hueIndex]) + { + return ColorNamesLight[hueIndex]; + } + else if (l >= LumLimitsForHueIndexLow[hueIndex]) + { + return ColorNamesMid[hueIndex]; + } + else + { + return ColorNamesDark[hueIndex]; + } + } + else if (l <= 170.0) + { + return (l <= 100.0) ? (uint)5117 : 5116; + } + else + { + return 5115; + } + } } diff --git a/src/Uno.UWP/UI/Color.cs b/src/Uno.UWP/UI/Color.cs index 79b5be8f4df6..f18f100218bc 100644 --- a/src/Uno.UWP/UI/Color.cs +++ b/src/Uno.UWP/UI/Color.cs @@ -81,6 +81,53 @@ public bool Equals(Color color) => internal uint AsUInt32() => _color; + internal HslColor ToHsl() + { + double r = R / 255.0; + double g = G / 255.0; + double b = B / 255.0; + + double max = Math.Max(Math.Max(r, g), b); + double min = Math.Min(Math.Min(r, g), b); + + double h = 0, s = 0, l = (max + min) / 2; + double delta = max - min; + + if (Math.Abs(delta) > double.Epsilon) + { + s = delta / (l > 0.5 ? (2.0 - max - min) : (max + min)); + + double deltaR = (((max - r) / 6) + (delta / 2)) / delta; + double deltaG = (((max - g) / 6) + (delta / 2)) / delta; + double deltaB = (((max - b) / 6) + (delta / 2)) / delta; + + if (Math.Abs(r - max) < double.Epsilon) + { + h = deltaB - deltaG; + } + else if (Math.Abs(g - max) < double.Epsilon) + { + h = (1.0 / 3.0) + deltaR - deltaB; + } + else + { + h = (2.0 / 3.0) + deltaG - deltaR; + } + + if (h < 0) + { + h += 1; + } + + if (h > 1) + { + h -= 1; + } + } + + return new(h, s, l); + } + string IFormattable.ToString(string format, IFormatProvider formatProvider) => ToString(format, formatProvider); private string ToString(string format, IFormatProvider formatProvider) => string.Format(formatProvider, "#{0:X2}{1:X2}{2:X2}{3:X2}", _a, _r, _g, _b); diff --git a/src/Uno.UWP/UI/HslColor.cs b/src/Uno.UWP/UI/HslColor.cs new file mode 100644 index 000000000000..dd43acca9c23 --- /dev/null +++ b/src/Uno.UWP/UI/HslColor.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Windows.UI; + +internal struct HslColor(double h, double s, double l) +{ + public double H { get; set; } = h; + public double S { get; set; } = s; + public double L { get; set; } = l; +}