diff --git a/src/ColorPicker.Models/ColorSpaceHelper.cs b/src/ColorPicker.Models/ColorSpaceHelper.cs index 16eab7e..4ad4467 100644 --- a/src/ColorPicker.Models/ColorSpaceHelper.cs +++ b/src/ColorPicker.Models/ColorSpaceHelper.cs @@ -91,6 +91,34 @@ public static Tuple RgbToHsl(double r, double g, double return new Tuple(h, s, l); } + /// + /// Converts RGB to Grayscale Tuple + /// + /// This method uses the luminosity method to calculate the grayscale + /// Red channel + /// Green channel + /// Blue channel + /// Values (0-1) in order: R, G, B (all same) + public static Tuple RgbToGrayTuple(double r, double g, double b) + { + // Using the luminosity method + var gray = RgbToGray(r, g, b); + return new Tuple(gray, gray, gray); + } + + /// + /// Converts RGB to Grayscale + /// + /// + /// + /// + /// Grayscale value (0-1) + public static double RgbToGray(double r, double g, double b) + { + // Using the luminosity method + return 0.21 * r + 0.72 * g + 0.07 * b; + } + /// /// Converts HSV to RGB /// @@ -143,6 +171,23 @@ public static Tuple HsvToHsl(double h, double s, double return new Tuple(h, hsl_s, hsl_l); } + /// + /// Converts HSV to Grayscale + /// + /// Hue, 0-360 + /// Saturation, 0-1 + /// Value, 0-1 + /// Values (0-1) in order: R, G, B (all same) + public static Tuple HsvToGray(double h, double s, double v) + { + //Converting HSV to RGB first to not only use the value component + var rgb = HsvToRgb(h, s, v); + // Using the luminosity method + var grey = RgbToGray(rgb.Item1, rgb.Item2, rgb.Item3); + + return new Tuple(grey, grey, grey); + } + /// /// Converts HSL to RGB /// @@ -199,5 +244,24 @@ public static Tuple HslToHsv(double h, double s, double hsv_s = 2 * (1 - l / hsv_v); return new Tuple(h, hsv_s, hsv_v); } + + /// + /// Converts HSL to Grayscale + /// + /// This method uses the lightness component of the HSL color to calculate the grayscale + /// value, effectively ignoring the hue and saturation components. + /// Hue, 0-360 + /// Saturation, 0-1 + /// Lightness, 0-1 + /// Values (0-1) in order: R, G, B (all same) + public static Tuple HslToGray(double h, double s, double l) + { + // Converting HSL to RGB first to not only use the lightness component + var rgb = HslToRgb(h, s, l); + // Using the luminosity method + var gray = RgbToGray(rgb.Item1, rgb.Item2, rgb.Item3); + + return new Tuple(gray, gray, gray); + } } } \ No newline at end of file diff --git a/src/ColorPicker/DualPickerControlBase.cs b/src/ColorPicker/DualPickerControlBase.cs index d7d9930..7e7cc80 100644 --- a/src/ColorPicker/DualPickerControlBase.cs +++ b/src/ColorPicker/DualPickerControlBase.cs @@ -1,7 +1,8 @@ -using System; +using ColorPicker.Models; +using System; +using System.ComponentModel; using System.Windows; using System.Windows.Media; -using ColorPicker.Models; namespace ColorPicker { @@ -45,34 +46,69 @@ public DualPickerControlBase() hintColorDecorator = new HintColorDecorator(this); SecondColor = new NotifyableColor(secondColorDecorator); - SecondColor.PropertyChanged += (sender, args) => + SecondColor.PropertyChanged += SecondColor_PropertyChanged; + + HintNotifyableColor = new NotifyableColor(hintColorDecorator); + HintNotifyableColor.PropertyChanged += HintNotifyableColor_PropertyChanged; + + IsEnabledChanged += (s, e) => { - if (!ignoreSecondaryColorChange) - { - ignoreSecondaryColorPropertyChange = true; + // Force update colors when IsEnabled changes + SecondColor_PropertyChanged(this, new PropertyChangedEventArgs(nameof(SecondColor))); + HintNotifyableColor_PropertyChanged(this, new PropertyChangedEventArgs(nameof(HintNotifyableColor))); + }; + } + + private void SecondColor_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (!ignoreSecondaryColorChange) + { + ignoreSecondaryColorPropertyChange = true; + if (IsEnabled) SecondaryColor = System.Windows.Media.Color.FromArgb( (byte)Math.Round(SecondColor.A), (byte)Math.Round(SecondColor.RGB_R), (byte)Math.Round(SecondColor.RGB_G), (byte)Math.Round(SecondColor.RGB_B)); - ignoreSecondaryColorPropertyChange = false; + else + { + var grayColor = SecondColor.RGB_R * 0.21 + + SecondColor.RGB_G * 0.72 + + SecondColor.RGB_B * 0.07; + SecondaryColor = System.Windows.Media.Color.FromArgb( + (byte)Math.Round(SecondColor.A), + (byte)grayColor, + (byte)grayColor, + (byte)grayColor); } - }; + ignoreSecondaryColorPropertyChange = false; + } + } - HintNotifyableColor = new NotifyableColor(hintColorDecorator); - HintNotifyableColor.PropertyChanged += (sender, args) => + private void HintNotifyableColor_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (!ignoreHintNotifyableColorChange) { - if (!ignoreHintNotifyableColorChange) - { - ignoreHintColorPropertyChange = true; + ignoreHintColorPropertyChange = true; + if (IsEnabled) HintColor = System.Windows.Media.Color.FromArgb( (byte)Math.Round(HintNotifyableColor.A), (byte)Math.Round(HintNotifyableColor.RGB_R), (byte)Math.Round(HintNotifyableColor.RGB_G), (byte)Math.Round(HintNotifyableColor.RGB_B)); - ignoreHintColorPropertyChange = false; + else + { + var grayColor = HintNotifyableColor.RGB_R * 0.21 + + HintNotifyableColor.RGB_G * 0.72 + + HintNotifyableColor.RGB_B * 0.07; + HintColor = System.Windows.Media.Color.FromArgb( + (byte)Math.Round(HintNotifyableColor.A), + (byte)grayColor, + (byte)grayColor, + (byte)grayColor); } - }; + ignoreHintColorPropertyChange = false; + } } public NotifyableColor SecondColor { get; set; } diff --git a/src/ColorPicker/PickerControlBase.cs b/src/ColorPicker/PickerControlBase.cs index 1e15191..a2f5c9f 100644 --- a/src/ColorPicker/PickerControlBase.cs +++ b/src/ColorPicker/PickerControlBase.cs @@ -41,14 +41,40 @@ public PickerControlBase() previousColor = newColor; } }; - ColorChanged += (sender, newColor) => + + var updateColorAction = new Action + ((sender, newColor) => { if (!ignoreColorChange) { ignoreColorPropertyChange = true; - SelectedColor = ((ColorRoutedEventArgs)newColor).Color; + if (IsEnabled) + SelectedColor = ((ColorRoutedEventArgs)newColor).Color; + else + { + var grayColor = ((ColorRoutedEventArgs)newColor).Color.R * 0.21 + + ((ColorRoutedEventArgs)newColor).Color.G * 0.72 + + ((ColorRoutedEventArgs)newColor).Color.B * 0.07; + SelectedColor = System.Windows.Media.Color.FromArgb( + ((ColorRoutedEventArgs)newColor).Color.A, + (byte)grayColor, + (byte)grayColor, + (byte)grayColor); + } ignoreColorPropertyChange = false; } + }); + + ColorChanged += (sender, args) => updateColorAction(sender, args); + IsEnabledChanged += (sender, args) => + { + var color = System.Windows.Media.Color.FromArgb( + (byte)Math.Round(Color.A), + (byte)Math.Round(Color.RGB_R), + (byte)Math.Round(Color.RGB_G), + (byte)Math.Round(Color.RGB_B)); + + updateColorAction(sender, new ColorRoutedEventArgs(ColorChangedEvent, color)); }; } diff --git a/src/ColorPicker/UIExtensions/HslColorSlider.cs b/src/ColorPicker/UIExtensions/HslColorSlider.cs index 4769a66..5e40919 100644 --- a/src/ColorPicker/UIExtensions/HslColorSlider.cs +++ b/src/ColorPicker/UIExtensions/HslColorSlider.cs @@ -73,27 +73,36 @@ private Color GetColorForSelectedArgb(int value) { case "H": { - var rgbtuple = ColorSpaceHelper.HslToRgb(value, 1.0, 0.5); + var rgbtuple = IsEnabled ? ColorSpaceHelper.HslToRgb(value, 1.0, 0.5) + : ColorSpaceHelper.HslToGray(value, 1.0, 0.5); double r = rgbtuple.Item1, g = rgbtuple.Item2, b = rgbtuple.Item3; return Color.FromArgb(255, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255)); } case "S": { - var rgbtuple = ColorSpaceHelper.HslToRgb(CurrentColorState.HSL_H, value / 255.0, - CurrentColorState.HSL_L); + var rgbtuple = IsEnabled ? ColorSpaceHelper.HslToRgb(CurrentColorState.HSL_H, value / 255.0, + CurrentColorState.HSL_L) + : ColorSpaceHelper.HslToGray(CurrentColorState.HSL_H, value / 255.0, + CurrentColorState.HSL_L); double r = rgbtuple.Item1, g = rgbtuple.Item2, b = rgbtuple.Item3; return Color.FromArgb(255, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255)); } case "L": { - var rgbtuple = ColorSpaceHelper.HslToRgb(CurrentColorState.HSL_H, CurrentColorState.HSL_S, - value / 255.0); + var rgbtuple = IsEnabled ? ColorSpaceHelper.HslToRgb(CurrentColorState.HSL_H, CurrentColorState.HSL_S, + value / 255.0) + : ColorSpaceHelper.HslToGray(CurrentColorState.HSL_H, CurrentColorState.HSL_S, + value / 255.0); double r = rgbtuple.Item1, g = rgbtuple.Item2, b = rgbtuple.Item3; return Color.FromArgb(255, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255)); } default: - return Color.FromArgb(255, (byte)(CurrentColorState.RGB_R * 255), - (byte)(CurrentColorState.RGB_G * 255), (byte)(CurrentColorState.RGB_B * 255)); + var rgbtupleDef = IsEnabled ? new System.Tuple(CurrentColorState.RGB_R, + CurrentColorState.RGB_G, CurrentColorState.RGB_B) + : ColorSpaceHelper.HslToGray(CurrentColorState.HSL_H, + CurrentColorState.HSL_S, CurrentColorState.HSL_L); + double rDef = rgbtupleDef.Item1, gDef = rgbtupleDef.Item2, bDef = rgbtupleDef.Item3; + return Color.FromArgb(255, (byte)(rDef * 255), (byte)(gDef * 255), (byte)(bDef * 255)); } } } diff --git a/src/ColorPicker/UIExtensions/HsvColorSlider.cs b/src/ColorPicker/UIExtensions/HsvColorSlider.cs index fbe5a7b..3a8dc8c 100644 --- a/src/ColorPicker/UIExtensions/HsvColorSlider.cs +++ b/src/ColorPicker/UIExtensions/HsvColorSlider.cs @@ -1,4 +1,5 @@ -using System.Windows; +using System; +using System.Windows; using System.Windows.Media; using ColorPicker.Models; @@ -58,27 +59,37 @@ private Color GetColorForSelectedArgb(int value) { case "H": { - var rgbtuple = ColorSpaceHelper.HsvToRgb(value, 1.0, 1.0); + var rgbtuple = IsEnabled ? ColorSpaceHelper.HsvToRgb(value, 1.0, 1.0) + : ColorSpaceHelper.HsvToGray(value, 1.0, 1.0); double r = rgbtuple.Item1, g = rgbtuple.Item2, b = rgbtuple.Item3; return Color.FromArgb(255, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255)); } case "S": { - var rgbtuple = ColorSpaceHelper.HsvToRgb(CurrentColorState.HSV_H, value / 255.0, + var rgbtuple = IsEnabled ? ColorSpaceHelper.HsvToRgb(CurrentColorState.HSV_H, value / 255.0, + CurrentColorState.HSV_V) + : ColorSpaceHelper.HsvToGray(CurrentColorState.HSV_H, value / 255.0, CurrentColorState.HSV_V); double r = rgbtuple.Item1, g = rgbtuple.Item2, b = rgbtuple.Item3; return Color.FromArgb(255, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255)); } case "V": { - var rgbtuple = ColorSpaceHelper.HsvToRgb(CurrentColorState.HSV_H, CurrentColorState.HSV_S, + var rgbtuple = IsEnabled ? ColorSpaceHelper.HsvToRgb(CurrentColorState.HSV_H, CurrentColorState.HSV_S, + value / 255.0) + : ColorSpaceHelper.HsvToGray(CurrentColorState.HSV_H, CurrentColorState.HSV_S, value / 255.0); double r = rgbtuple.Item1, g = rgbtuple.Item2, b = rgbtuple.Item3; return Color.FromArgb(255, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255)); } default: - return Color.FromArgb((byte)(CurrentColorState.A * 255), (byte)(CurrentColorState.RGB_R * 255), - (byte)(CurrentColorState.RGB_G * 255), (byte)(CurrentColorState.RGB_B * 255)); + var rgbtupleDef = IsEnabled ? new Tuple(CurrentColorState.RGB_R, + CurrentColorState.RGB_G, CurrentColorState.RGB_B) + : ColorSpaceHelper.RgbToGrayTuple(CurrentColorState.RGB_R, + CurrentColorState.RGB_G, CurrentColorState.RGB_B); + double rDef = rgbtupleDef.Item1, gDef = rgbtupleDef.Item2, bDef = rgbtupleDef.Item3; + return Color.FromArgb((byte)(CurrentColorState.A * 255), (byte)(rDef * 255), + (byte)(gDef * 255), (byte)(bDef * 255)); } } } diff --git a/src/ColorPicker/UIExtensions/PreviewColorSlider.cs b/src/ColorPicker/UIExtensions/PreviewColorSlider.cs index 92a88da..377fda8 100644 --- a/src/ColorPicker/UIExtensions/PreviewColorSlider.cs +++ b/src/ColorPicker/UIExtensions/PreviewColorSlider.cs @@ -31,6 +31,10 @@ public PreviewColorSlider() LargeChange = 10; MinHeight = 12; PreviewMouseWheel += OnPreviewMouseWheel; + IsEnabledChanged += (s, e) => + { + GenerateBackground(); + }; } protected virtual bool RefreshGradient => true; diff --git a/src/ColorPicker/UIExtensions/RgbColorSlider.cs b/src/ColorPicker/UIExtensions/RgbColorSlider.cs index b1ef942..cb7127a 100644 --- a/src/ColorPicker/UIExtensions/RgbColorSlider.cs +++ b/src/ColorPicker/UIExtensions/RgbColorSlider.cs @@ -34,13 +34,14 @@ private Color GetColorForSelectedArgb(int value) var r = (byte)(CurrentColorState.RGB_R * 255); var g = (byte)(CurrentColorState.RGB_G * 255); var b = (byte)(CurrentColorState.RGB_B * 255); + var gray = (byte)(0.21 * r + 0.72 * g + 0.07 * b); switch (SliderArgbType) { - case "A": return Color.FromArgb((byte)value, r, g, b); - case "R": return Color.FromArgb(255, (byte)value, g, b); - case "G": return Color.FromArgb(255, r, (byte)value, b); - case "B": return Color.FromArgb(255, r, g, (byte)value); - default: return Color.FromArgb(a, r, g, b); + case "A": return IsEnabled ? Color.FromArgb((byte)value, r, g, b) : Color.FromArgb((byte)value, gray, gray, gray); + case "R": return IsEnabled ? Color.FromArgb(255, (byte)value, g, b) : Color.FromArgb(255, (byte)value, (byte)value, (byte)value); + case "G": return IsEnabled ? Color.FromArgb(255, r, (byte)value, b) : Color.FromArgb(255, (byte)value, (byte)value, (byte)value); + case "B": return IsEnabled ? Color.FromArgb(255, r, g, (byte)value) : Color.FromArgb(255, (byte)value, (byte)value, (byte)value); + default: return IsEnabled ? Color.FromArgb(a, r, g, b) : Color.FromArgb(a, gray, gray, gray); } ; diff --git a/src/ColorPicker/UserControls/HueSlider.xaml b/src/ColorPicker/UserControls/HueSlider.xaml index ce76269..c594b92 100644 --- a/src/ColorPicker/UserControls/HueSlider.xaml +++ b/src/ColorPicker/UserControls/HueSlider.xaml @@ -6,9 +6,15 @@ mc:Ignorable="d" x:ClassModifier="internal" x:Name="uc"> + + + + - @@ -21,6 +27,16 @@ + + + diff --git a/src/ColorPicker/UserControls/SquareSlider.xaml.cs b/src/ColorPicker/UserControls/SquareSlider.xaml.cs index 88bcd3e..f835e33 100644 --- a/src/ColorPicker/UserControls/SquareSlider.xaml.cs +++ b/src/ColorPicker/UserControls/SquareSlider.xaml.cs @@ -40,6 +40,7 @@ public SquareSlider() GradientBitmap = new WriteableBitmap(32, 32, 96, 96, PixelFormats.Rgb24, null); InitializeComponent(); RecalculateGradient(); + IsEnabledChanged += OnIsEnabledChanged; } public double Hue @@ -134,6 +135,24 @@ private static void OnHueChanged(DependencyObject d, DependencyPropertyChangedEv ((SquareSlider)d).RecalculateGradient(); } + private void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) + { + var sqs = (SquareSlider)sender; + + if (IsEnabled) + if (sqs.PickerType == PickerType.HSV) + sqs.colorSpaceConversionMethod = ColorSpaceHelper.HsvToRgb; + else + sqs.colorSpaceConversionMethod = ColorSpaceHelper.HslToRgb; + else + if (sqs.PickerType == PickerType.HSV) + sqs.colorSpaceConversionMethod = ColorSpaceHelper.HsvToGray; + else + sqs.colorSpaceConversionMethod = ColorSpaceHelper.HslToGray; + + sqs.RecalculateGradient(); + } + private void OnMouseDown(object sender, MouseButtonEventArgs e) { ((UIElement)sender).CaptureMouse();