diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 52f203a9de..43447793a0 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1982,4 +1982,12 @@ zoomit ZOOMITX ZXk ZXNs -zzz \ No newline at end of file +zzz +ACIE +AOklab +BCIE +BOklab +culori +Evercoder +LCh +CIELCh diff --git a/src/common/ManagedCommon/ColorFormatHelper.cs b/src/common/ManagedCommon/ColorFormatHelper.cs index 08e62b921d..471104f215 100644 --- a/src/common/ManagedCommon/ColorFormatHelper.cs +++ b/src/common/ManagedCommon/ColorFormatHelper.cs @@ -141,6 +141,40 @@ namespace ManagedCommon return lab; } + /// + /// Convert a given to a Oklab color + /// + /// The to convert + /// The perceptual lightness [0..1] and two chromaticities [-0.5..0.5] + public static (double Lightness, double ChromaticityA, double ChromaticityB) ConvertToOklabColor(Color color) + { + var linear = ConvertSRGBToLinearRGB(color.R / 255d, color.G / 255d, color.B / 255d); + var oklab = GetOklabColorFromLinearRGB(linear.R, linear.G, linear.B); + return oklab; + } + + /// + /// Convert a given to a Oklch color + /// + /// The to convert + /// The perceptual lightness [0..1], the chroma [0..0.5], and the hue angle [0°..360°] + public static (double Lightness, double Chroma, double Hue) ConvertToOklchColor(Color color) + { + var oklab = ConvertToOklabColor(color); + var oklch = GetOklchColorFromOklab(oklab.Lightness, oklab.ChromaticityA, oklab.ChromaticityB); + + return oklch; + } + + public static (double R, double G, double B) ConvertSRGBToLinearRGB(double r, double g, double b) + { + // inverse companding, gamma correction must be undone + double rLinear = (r > 0.04045) ? Math.Pow((r + 0.055) / 1.055, 2.4) : (r / 12.92); + double gLinear = (g > 0.04045) ? Math.Pow((g + 0.055) / 1.055, 2.4) : (g / 12.92); + double bLinear = (b > 0.04045) ? Math.Pow((b + 0.055) / 1.055, 2.4) : (b / 12.92); + return (rLinear, gLinear, bLinear); + } + /// /// Convert a given to a CIE XYZ color (XYZ) /// The constants of the formula matches this Wikipedia page, but at a higher precision: @@ -156,10 +190,7 @@ namespace ManagedCommon double g = color.G / 255d; double b = color.B / 255d; - // inverse companding, gamma correction must be undone - double rLinear = (r > 0.04045) ? Math.Pow((r + 0.055) / 1.055, 2.4) : (r / 12.92); - double gLinear = (g > 0.04045) ? Math.Pow((g + 0.055) / 1.055, 2.4) : (g / 12.92); - double bLinear = (b > 0.04045) ? Math.Pow((b + 0.055) / 1.055, 2.4) : (b / 12.92); + (double rLinear, double gLinear, double bLinear) = ConvertSRGBToLinearRGB(r, g, b); return ( (rLinear * 0.41239079926595948) + (gLinear * 0.35758433938387796) + (bLinear * 0.18048078840183429), @@ -210,6 +241,63 @@ namespace ManagedCommon return (l, a, b); } + /// + /// Convert a linear RGB color to an Oklab color. + /// The constants of this formula come from https://github.com/Evercoder/culori/blob/2bedb8f0507116e75f844a705d0b45cf279b15d0/src/oklab/convertLrgbToOklab.js + /// and the implementation is based on https://bottosson.github.io/posts/oklab/ + /// + /// Linear R value + /// Linear G value + /// Linear B value + /// The perceptual lightness [0..1] and two chromaticities [-0.5..0.5] + private static (double Lightness, double ChromaticityA, double ChromaticityB) + GetOklabColorFromLinearRGB(double r, double g, double b) + { + double l = (0.41222147079999993 * r) + (0.5363325363 * g) + (0.0514459929 * b); + double m = (0.2119034981999999 * r) + (0.6806995450999999 * g) + (0.1073969566 * b); + double s = (0.08830246189999998 * r) + (0.2817188376 * g) + (0.6299787005000002 * b); + + double l_ = Math.Cbrt(l); + double m_ = Math.Cbrt(m); + double s_ = Math.Cbrt(s); + + return ( + (0.2104542553 * l_) + (0.793617785 * m_) - (0.0040720468 * s_), + (1.9779984951 * l_) - (2.428592205 * m_) + (0.4505937099 * s_), + (0.0259040371 * l_) + (0.7827717662 * m_) - (0.808675766 * s_) + ); + } + + /// + /// Convert an Oklab color from Cartesian form to its polar form Oklch + /// https://bottosson.github.io/posts/oklab/#the-oklab-color-space + /// + /// The + /// The + /// The + /// The perceptual lightness [0..1], the chroma [0..0.5], and the hue angle [0°..360°] + private static (double Lightness, double Chroma, double Hue) + GetOklchColorFromOklab(double lightness, double chromaticity_a, double chromaticity_b) + { + return GetLCHColorFromLAB(lightness, chromaticity_a, chromaticity_b); + } + + /// + /// Convert a color in Cartesian form (Lab) to its polar form (LCh) + /// + /// The + /// The + /// The + /// The lightness, chroma, and hue angle + private static (double Lightness, double Chroma, double Hue) + GetLCHColorFromLAB(double lightness, double chromaticity_a, double chromaticity_b) + { + // Lab to LCh transformation + double chroma = Math.Sqrt(Math.Pow(chromaticity_a, 2) + Math.Pow(chromaticity_b, 2)); + double hue = Math.Round(chroma, 3) == 0 ? 0.0 : ((Math.Atan2(chromaticity_b, chromaticity_a) * 180d / Math.PI) + 360d) % 360d; + return (lightness, chroma, hue); + } + /// /// Convert a given to a natural color (hue, whiteness, blackness) /// @@ -276,12 +364,17 @@ namespace ManagedCommon { "Br", 'p' }, // brightness percent { "In", 'p' }, // intensity percent { "Ll", 'p' }, // lightness (HSL) percent - { "Lc", 'p' }, // lightness(CIELAB)percent { "Va", 'p' }, // value percent { "Wh", 'p' }, // whiteness percent { "Bn", 'p' }, // blackness percent - { "Ca", 'p' }, // chromaticityA percent - { "Cb", 'p' }, // chromaticityB percent + { "Lc", 'p' }, // lightness (CIE) percent + { "Ca", 'p' }, // chromaticityA (CIELAB) percent + { "Cb", 'p' }, // chromaticityB (CIELAB) percent + { "Lo", 'p' }, // lightness (Oklab/Oklch) percent + { "Oa", 'p' }, // chromaticityA (Oklab) percent + { "Ob", 'p' }, // chromaticityB (Oklab) percent + { "Oc", 'p' }, // chroma (Oklch) percent + { "Oh", 'p' }, // hue angle (Oklch) percent { "Xv", 'i' }, // X value int { "Yv", 'i' }, // Y value int { "Zv", 'i' }, // Z value int @@ -424,6 +517,10 @@ namespace ManagedCommon var (lightnessC, _, _) = ConvertToCIELABColor(color); lightnessC = Math.Round(lightnessC, 2); return lightnessC.ToString(CultureInfo.InvariantCulture); + case "Lo": + var (lightnessO, _, _) = ConvertToOklabColor(color); + lightnessO = Math.Round(lightnessO, 2); + return lightnessO.ToString(CultureInfo.InvariantCulture); case "Wh": var (_, whiteness, _) = ConvertToHWBColor(color); whiteness = Math.Round(whiteness * 100); @@ -440,6 +537,22 @@ namespace ManagedCommon var (_, _, chromaticityB) = ConvertToCIELABColor(color); chromaticityB = Math.Round(chromaticityB, 2); return chromaticityB.ToString(CultureInfo.InvariantCulture); + case "Oa": + var (_, chromaticityAOklab, _) = ConvertToOklabColor(color); + chromaticityAOklab = Math.Round(chromaticityAOklab, 2); + return chromaticityAOklab.ToString(CultureInfo.InvariantCulture); + case "Ob": + var (_, _, chromaticityBOklab) = ConvertToOklabColor(color); + chromaticityBOklab = Math.Round(chromaticityBOklab, 2); + return chromaticityBOklab.ToString(CultureInfo.InvariantCulture); + case "Oc": + var (_, chromaOklch, _) = ConvertToOklchColor(color); + chromaOklch = Math.Round(chromaOklch, 2); + return chromaOklch.ToString(CultureInfo.InvariantCulture); + case "Oh": + var (_, _, hueOklch) = ConvertToOklchColor(color); + hueOklch = Math.Round(hueOklch, 2); + return hueOklch.ToString(CultureInfo.InvariantCulture); case "Xv": var (x, _, _) = ConvertToCIEXYZColor(color); x = Math.Round(x * 100, 4); @@ -495,8 +608,10 @@ namespace ManagedCommon case "HSI": return "hsi(%Hu, %Si%, %In%)"; case "HWB": return "hwb(%Hu, %Wh%, %Bn%)"; case "NCol": return "%Hn, %Wh%, %Bn%"; - case "CIELAB": return "CIELab(%Lc, %Ca, %Cb)"; case "CIEXYZ": return "XYZ(%Xv, %Yv, %Zv)"; + case "CIELAB": return "CIELab(%Lc, %Ca, %Cb)"; + case "Oklab": return "oklab(%Lo, %Oa, %Ob)"; + case "Oklch": return "oklch(%Lo, %Oc, %Oh)"; case "VEC4": return "(%Reff, %Grff, %Blff, 1f)"; case "Decimal": return "%Dv"; case "HEX Int": return "0xFF%ReX%GrX%BlX"; diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/ColorRepresentationHelper.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/ColorRepresentationHelper.cs index 82b238993d..3f0feaaae3 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/ColorRepresentationHelper.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/ColorRepresentationHelper.cs @@ -243,6 +243,40 @@ namespace ColorPicker.Helpers $", {chromaticityB.ToString(CultureInfo.InvariantCulture)})"; } + /// + /// Returns a representation of a Oklab color + /// + /// The for the Oklab color presentation + /// A representation of a Oklab color + private static string ColorToOklab(Color color) + { + var (lightness, chromaticityA, chromaticityB) = ColorFormatHelper.ConvertToOklabColor(color); + lightness = Math.Round(lightness, 2); + chromaticityA = Math.Round(chromaticityA, 2); + chromaticityB = Math.Round(chromaticityB, 2); + + return $"oklab({lightness.ToString(CultureInfo.InvariantCulture)}" + + $", {chromaticityA.ToString(CultureInfo.InvariantCulture)}" + + $", {chromaticityB.ToString(CultureInfo.InvariantCulture)})"; + } + + /// + /// Returns a representation of a CIE LCh color + /// + /// The for the CIE LCh color presentation + /// A representation of a CIE LCh color + private static string ColorToOklch(Color color) + { + var (lightness, chroma, hue) = ColorFormatHelper.ConvertToOklchColor(color); + lightness = Math.Round(lightness, 2); + chroma = Math.Round(chroma, 2); + hue = Math.Round(hue, 2); + + return $"oklch({lightness.ToString(CultureInfo.InvariantCulture)}" + + $", {chroma.ToString(CultureInfo.InvariantCulture)}" + + $", {hue.ToString(CultureInfo.InvariantCulture)})"; + } + /// /// Returns a representation of a CIE XYZ color /// diff --git a/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs b/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs index 129f365e0d..2f8d5a2348 100644 --- a/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs +++ b/src/modules/colorPicker/ColorPickerUI/ViewModels/ColorEditorViewModel.cs @@ -301,6 +301,12 @@ namespace ColorPicker.ViewModels FormatName = ColorRepresentationType.NCol.ToString(), Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.NCol.ToString()), }); + _allColorRepresentations.Add( + new ColorFormatModel() + { + FormatName = ColorRepresentationType.CIEXYZ.ToString(), + Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.CIEXYZ.ToString()), + }); _allColorRepresentations.Add( new ColorFormatModel() { @@ -310,8 +316,14 @@ namespace ColorPicker.ViewModels _allColorRepresentations.Add( new ColorFormatModel() { - FormatName = ColorRepresentationType.CIEXYZ.ToString(), - Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.CIEXYZ.ToString()), + FormatName = ColorRepresentationType.Oklab.ToString(), + Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.Oklab.ToString()), + }); + _allColorRepresentations.Add( + new ColorFormatModel() + { + FormatName = ColorRepresentationType.Oklch.ToString(), + Convert = (Color color) => ColorRepresentationHelper.GetStringRepresentationFromMediaColor(color, ColorRepresentationType.Oklch.ToString()), }); _allColorRepresentations.Add( new ColorFormatModel() diff --git a/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorConverterTest.cs b/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorConverterTest.cs index eaa5369dd6..288ed0f599 100644 --- a/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorConverterTest.cs +++ b/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorConverterTest.cs @@ -364,9 +364,6 @@ namespace Microsoft.ColorPicker.UnitTests [DataRow("8080FF", 59.20, 33.10, -63.46)] // blue [DataRow("BF40BF", 50.10, 65.50, -41.48)] // magenta [DataRow("BFBF00", 75.04, -17.35, 76.03)] // yellow - [DataRow("008000", 46.23, -51.70, 49.90)] // green - [DataRow("8080FF", 59.20, 33.10, -63.46)] // blue - [DataRow("BF40BF", 50.10, 65.50, -41.48)] // magenta [DataRow("0048BA", 34.35, 27.94, -64.80)] // absolute zero [DataRow("B0BF1A", 73.91, -23.39, 71.15)] // acid green [DataRow("D0FF14", 93.87, -40.20, 88.97)] // arctic lime @@ -401,13 +398,121 @@ namespace Microsoft.ColorPicker.UnitTests var result = ColorFormatHelper.ConvertToCIELABColor(color); // lightness[0..100] - Assert.AreEqual(Math.Round(result.Lightness, 2), lightness); + Assert.AreEqual(lightness, Math.Round(result.Lightness, 2)); // chromaticityA[-128..127] - Assert.AreEqual(Math.Round(result.ChromaticityA, 2), chromaticityA); + Assert.AreEqual(chromaticityA, Math.Round(result.ChromaticityA, 2)); // chromaticityB[-128..127] - Assert.AreEqual(Math.Round(result.ChromaticityB, 2), chromaticityB); + Assert.AreEqual(chromaticityB, Math.Round(result.ChromaticityB, 2)); + } + + // Test data calculated using https://oklch.com (which uses https://github.com/Evercoder/culori) + [TestMethod] + [DataRow("FFFFFF", 1.00, 0.00, 0.00)] // white + [DataRow("808080", 0.6, 0.00, 0.00)] // gray + [DataRow("000000", 0.00, 0.00, 0.00)] // black + [DataRow("FF0000", 0.628, 0.22, 0.13)] // red + [DataRow("008000", 0.52, -0.14, 0.11)] // green + [DataRow("80FFFF", 0.928, -0.11, -0.03)] // cyan + [DataRow("8080FF", 0.661, 0.03, -0.18)] // blue + [DataRow("BF40BF", 0.598, 0.18, -0.11)] // magenta + [DataRow("BFBF00", 0.779, -0.06, 0.16)] // yellow + [DataRow("0048BA", 0.444, -0.03, -0.19)] // absolute zero + [DataRow("B0BF1A", 0.767, -0.07, 0.15)] // acid green + [DataRow("D0FF14", 0.934, -0.12, 0.19)] // arctic lime + [DataRow("1B4D3E", 0.382, -0.06, 0.01)] // brunswick green + [DataRow("FFEF00", 0.935, -0.05, 0.19)] // canary yellow + [DataRow("FFA600", 0.794, 0.06, 0.16)] // cheese + [DataRow("1A2421", 0.25, -0.02, 0)] // dark jungle green + [DataRow("003399", 0.371, -0.02, -0.17)] // dark powder blue + [DataRow("D70A53", 0.563, 0.22, 0.04)] // debian red + [DataRow("80FFD5", 0.916, -0.13, 0.02)] // fathom secret green + [DataRow("EFDFBB", 0.907, 0, 0.05)] // dutch white + [DataRow("5218FA", 0.489, 0.05, -0.28)] // han purple + [DataRow("FF496C", 0.675, 0.21, 0.05)] // infra red + [DataRow("545AA7", 0.5, 0.02, -0.12)] // liberty + [DataRow("E6A8D7", 0.804, 0.09, -0.04)] // light orchid + [DataRow("ADDFAD", 0.856, -0.07, 0.05)] // light moss green + [DataRow("E3F988", 0.942, -0.07, 0.12)] // mindaro + public void ColorRGBtoOklabTest(string hexValue, double lightness, double chromaticityA, double chromaticityB) + { + if (string.IsNullOrWhiteSpace(hexValue)) + { + Assert.IsNotNull(hexValue); + } + + Assert.IsTrue(hexValue.Length >= 6); + + var red = int.Parse(hexValue.AsSpan(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); + var green = int.Parse(hexValue.AsSpan(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); + var blue = int.Parse(hexValue.AsSpan(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); + + var color = Color.FromArgb(255, red, green, blue); + var result = ColorFormatHelper.ConvertToOklabColor(color); + + // lightness[0..1] + Assert.AreEqual(lightness, Math.Round(result.Lightness, 3)); + + // chromaticityA[-0.5..0.5] + Assert.AreEqual(chromaticityA, Math.Round(result.ChromaticityA, 2)); + + // chromaticityB[-0.5..0.5] + Assert.AreEqual(chromaticityB, Math.Round(result.ChromaticityB, 2)); + } + + // Test data calculated using https://oklch.com (which uses https://github.com/Evercoder/culori) + [TestMethod] + [DataRow("FFFFFF", 1.00, 0.00, 0.00)] // white + [DataRow("808080", 0.6, 0.00, 0.00)] // gray + [DataRow("000000", 0.00, 0.00, 0.00)] // black + [DataRow("FF0000", 0.628, 0.258, 29.23)] // red + [DataRow("008000", 0.52, 0.177, 142.5)] // green + [DataRow("80FFFF", 0.928, 0.113, 195.38)] // cyan + [DataRow("8080FF", 0.661, 0.184, 280.13)] // blue + [DataRow("BF40BF", 0.598, 0.216, 327.86)] // magenta + [DataRow("BFBF00", 0.779, 0.17, 109.77)] // yellow + [DataRow("0048BA", 0.444, 0.19, 260.86)] // absolute zero + [DataRow("B0BF1A", 0.767, 0.169, 115.4)] // acid green + [DataRow("D0FF14", 0.934, 0.224, 122.28)] // arctic lime + [DataRow("1B4D3E", 0.382, 0.06, 170.28)] // brunswick green + [DataRow("FFEF00", 0.935, 0.198, 104.67)] // canary yellow + [DataRow("FFA600", 0.794, 0.171, 71.19)] // cheese + [DataRow("1A2421", 0.25, 0.015, 174.74)] // dark jungle green + [DataRow("003399", 0.371, 0.173, 262.12)] // dark powder blue + [DataRow("D70A53", 0.563, 0.222, 11.5)] // debian red + [DataRow("80FFD5", 0.916, 0.129, 169.38)] // fathom secret green + [DataRow("EFDFBB", 0.907, 0.05, 86.89)] // dutch white + [DataRow("5218FA", 0.489, 0.286, 279.13)] // han purple + [DataRow("FF496C", 0.675, 0.217, 14.37)] // infra red + [DataRow("545AA7", 0.5, 0.121, 277.7)] // liberty + [DataRow("E6A8D7", 0.804, 0.095, 335.4)] // light orchid + [DataRow("ADDFAD", 0.856, 0.086, 144.78)] // light moss green + [DataRow("E3F988", 0.942, 0.141, 118.24)] // mindaro + public void ColorRGBtoOklchTest(string hexValue, double lightness, double chroma, double hue) + { + if (string.IsNullOrWhiteSpace(hexValue)) + { + Assert.IsNotNull(hexValue); + } + + Assert.IsTrue(hexValue.Length >= 6); + + var red = int.Parse(hexValue.AsSpan(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); + var green = int.Parse(hexValue.AsSpan(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); + var blue = int.Parse(hexValue.AsSpan(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); + + var color = Color.FromArgb(255, red, green, blue); + var result = ColorFormatHelper.ConvertToOklchColor(color); + + // lightness[0..1] + Assert.AreEqual(lightness, Math.Round(result.Lightness, 3)); + + // chroma[0..0.5] + Assert.AreEqual(chroma, Math.Round(result.Chroma, 3)); + + // hue[0°..360°] + Assert.AreEqual(hue, Math.Round(result.Hue, 2)); } // The following results are computed using LittleCMS2, an open-source color management engine, @@ -428,9 +533,6 @@ namespace Microsoft.ColorPicker.UnitTests [DataRow("8080FF", 34.6688, 27.2469, 98.0434)] // blue [DataRow("BF40BF", 32.7217, 18.5062, 51.1405)] // magenta [DataRow("BFBF00", 40.1154, 48.3384, 7.2171)] // yellow - [DataRow("008000", 7.7188, 15.4377, 2.5729)] // green - [DataRow("8080FF", 34.6688, 27.2469, 98.0434)] // blue - [DataRow("BF40BF", 32.7217, 18.5062, 51.1405)] // magenta [DataRow("0048BA", 11.1792, 8.1793, 47.4455)] // absolute zero [DataRow("B0BF1A", 36.7205, 46.5663, 8.0311)] // acid green [DataRow("D0FF14", 61.8965, 84.9797, 13.8037)] // arctic lime diff --git a/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorRepresentationHelperTest.cs b/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorRepresentationHelperTest.cs index a96310dc82..f1f0c99e3d 100644 --- a/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorRepresentationHelperTest.cs +++ b/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorRepresentationHelperTest.cs @@ -23,8 +23,10 @@ namespace Microsoft.ColorPicker.UnitTests [DataRow("HSV", "hsv(0, 0%, 0%)")] [DataRow("HWB", "hwb(0, 0%, 100%)")] [DataRow("RGB", "rgb(0, 0, 0)")] - [DataRow("CIELAB", "CIELab(0, 0, 0)")] [DataRow("CIEXYZ", "XYZ(0, 0, 0)")] + [DataRow("CIELAB", "CIELab(0, 0, 0)")] + [DataRow("Oklab", "oklab(0, 0, 0)")] + [DataRow("Oklch", "oklch(0, 0, 0)")] [DataRow("VEC4", "(0f, 0f, 0f, 1f)")] [DataRow("Decimal", "0")] [DataRow("HEX Int", "0xFF000000")] diff --git a/src/settings-ui/Settings.UI.Library/ColorPickerProperties.cs b/src/settings-ui/Settings.UI.Library/ColorPickerProperties.cs index 0d3fc918d6..b82ef56888 100644 --- a/src/settings-ui/Settings.UI.Library/ColorPickerProperties.cs +++ b/src/settings-ui/Settings.UI.Library/ColorPickerProperties.cs @@ -32,8 +32,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library VisibleColorFormats.Add("HSI", new KeyValuePair(false, ColorFormatHelper.GetDefaultFormat("HSI"))); VisibleColorFormats.Add("HWB", new KeyValuePair(false, ColorFormatHelper.GetDefaultFormat("HWB"))); VisibleColorFormats.Add("NCol", new KeyValuePair(false, ColorFormatHelper.GetDefaultFormat("NCol"))); - VisibleColorFormats.Add("CIELAB", new KeyValuePair(false, ColorFormatHelper.GetDefaultFormat("CIELAB"))); VisibleColorFormats.Add("CIEXYZ", new KeyValuePair(false, ColorFormatHelper.GetDefaultFormat("CIEXYZ"))); + VisibleColorFormats.Add("CIELAB", new KeyValuePair(false, ColorFormatHelper.GetDefaultFormat("CIELAB"))); + VisibleColorFormats.Add("Oklab", new KeyValuePair(false, ColorFormatHelper.GetDefaultFormat("Oklab"))); + VisibleColorFormats.Add("Oklch", new KeyValuePair(false, ColorFormatHelper.GetDefaultFormat("Oklch"))); VisibleColorFormats.Add("VEC4", new KeyValuePair(false, ColorFormatHelper.GetDefaultFormat("VEC4"))); VisibleColorFormats.Add("Decimal", new KeyValuePair(false, ColorFormatHelper.GetDefaultFormat("Decimal"))); VisibleColorFormats.Add("HEX Int", new KeyValuePair(false, ColorFormatHelper.GetDefaultFormat("HEX Int"))); diff --git a/src/settings-ui/Settings.UI.Library/Enumerations/ColorRepresentationType.cs b/src/settings-ui/Settings.UI.Library/Enumerations/ColorRepresentationType.cs index 09ef003e88..7e57f5a730 100644 --- a/src/settings-ui/Settings.UI.Library/Enumerations/ColorRepresentationType.cs +++ b/src/settings-ui/Settings.UI.Library/Enumerations/ColorRepresentationType.cs @@ -80,5 +80,20 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Enumerations /// Color presentation as an 8-digit hexadecimal integer (0xFFFFFFFF) /// HexInteger = 13, + + /// + /// Color representation as CIELCh color space (L[0..100], C[0..230], h[0°..360°]) + /// + CIELCh = 14, + + /// + /// Color representation as Oklab color space (L[0..1], a[-0.5..0.5], b[-0.5..0.5]) + /// + Oklab = 15, + + /// + /// Color representation as Oklch color space (L[0..1], C[0..0.5], h[0°..360°]) + /// + Oklch = 16, } } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ColorFormatEditor.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ColorFormatEditor.xaml index 32f0ee488e..ea6d1c9f22 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ColorFormatEditor.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ColorFormatEditor.xaml @@ -32,7 +32,8 @@ Style="{StaticResource CaptionTextBlockStyle}" Text="{x:Bind Description}" TextTrimming="CharacterEllipsis" - TextWrapping="NoWrap" /> + TextWrapping="NoWrap" + ToolTipService.ToolTip="{x:Bind Description}" /> diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ColorFormatEditor.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ColorFormatEditor.xaml.cs index 76681d8b46..475d399674 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ColorFormatEditor.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ColorFormatEditor.xaml.cs @@ -47,12 +47,17 @@ namespace Microsoft.PowerToys.Settings.UI.Controls new ColorFormatParameter() { Parameter = "%In", Description = resourceLoader.GetString("Help_intensity") }, new ColorFormatParameter() { Parameter = "%Hn", Description = resourceLoader.GetString("Help_hueNat") }, new ColorFormatParameter() { Parameter = "%Ll", Description = resourceLoader.GetString("Help_lightnessNat") }, - new ColorFormatParameter() { Parameter = "%Lc", Description = resourceLoader.GetString("Help_lightnessCIE") }, new ColorFormatParameter() { Parameter = "%Va", Description = resourceLoader.GetString("Help_value") }, new ColorFormatParameter() { Parameter = "%Wh", Description = resourceLoader.GetString("Help_whiteness") }, new ColorFormatParameter() { Parameter = "%Bn", Description = resourceLoader.GetString("Help_blackness") }, - new ColorFormatParameter() { Parameter = "%Ca", Description = resourceLoader.GetString("Help_chromaticityA") }, - new ColorFormatParameter() { Parameter = "%Cb", Description = resourceLoader.GetString("Help_chromaticityB") }, + new ColorFormatParameter() { Parameter = "%Lc", Description = resourceLoader.GetString("Help_lightnessCIE") }, + new ColorFormatParameter() { Parameter = "%Ca", Description = resourceLoader.GetString("Help_chromaticityACIE") }, + new ColorFormatParameter() { Parameter = "%Cb", Description = resourceLoader.GetString("Help_chromaticityBCIE") }, + new ColorFormatParameter() { Parameter = "%Lo", Description = resourceLoader.GetString("Help_lightnessOklab") }, + new ColorFormatParameter() { Parameter = "%Oa", Description = resourceLoader.GetString("Help_chromaticityAOklab") }, + new ColorFormatParameter() { Parameter = "%Ob", Description = resourceLoader.GetString("Help_chromaticityBOklab") }, + new ColorFormatParameter() { Parameter = "%Oc", Description = resourceLoader.GetString("Help_chromaOklch") }, + new ColorFormatParameter() { Parameter = "%Oh", Description = resourceLoader.GetString("Help_hueOklch") }, new ColorFormatParameter() { Parameter = "%Xv", Description = resourceLoader.GetString("Help_X_value") }, new ColorFormatParameter() { Parameter = "%Yv", Description = resourceLoader.GetString("Help_Y_value") }, new ColorFormatParameter() { Parameter = "%Zv", Description = resourceLoader.GetString("Help_Z_value") }, diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index a95cdb86cd..694603707c 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -1660,11 +1660,11 @@ Made with 💗 by Microsoft and the PowerToys community. blackness - - chromaticityA + + chromaticity A (CIE Lab) - - chromaticityB + + chromaticity B (CIE Lab) X value @@ -4460,7 +4460,7 @@ Activate by holding the key for the character you want to add an accent to, then Commonly used variables New+ commonly used variables header in the flyout info card - + Year, represented by a full four or five digits, depending on the calendar used. New+ description of the year $YYYY variable - casing of $YYYY is important @@ -4999,4 +4999,25 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m Go to Command Palette settings to customize the activation shortcut. + + chroma (CIE LCh) + + + hue (CIE LCh) + + + lightness (Oklab/Oklch) + + + chromaticity A (Oklab) + + + chromaticity B (Oklab) + + + chroma (Oklch) + + + hue (Oklch) + \ No newline at end of file