Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions src/ColorPicker.Models/ColorSpaceHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,34 @@ public static Tuple<double, double, double> RgbToHsl(double r, double g, double
return new Tuple<double, double, double>(h, s, l);
}

/// <summary>
/// Converts RGB to Grayscale Tuple
/// </summary>
/// <remarks>This method uses the luminosity method to calculate the grayscale</remarks>
/// <param name="r">Red channel</param>
/// <param name="g">Green channel</param>
/// <param name="b">Blue channel</param>
/// <returns>Values (0-1) in order: R, G, B (all same)</returns>
public static Tuple<double, double, double> RgbToGrayTuple(double r, double g, double b)
{
// Using the luminosity method
var gray = RgbToGray(r, g, b);
return new Tuple<double, double, double>(gray, gray, gray);
}

/// <summary>
/// Converts RGB to Grayscale
/// </summary>
/// <param name="r"></param>
/// <param name="g"></param>
/// <param name="b"></param>
/// <returns>Grayscale value (0-1)</returns>
public static double RgbToGray(double r, double g, double b)
{
// Using the luminosity method
return 0.21 * r + 0.72 * g + 0.07 * b;
}

/// <summary>
/// Converts HSV to RGB
/// </summary>
Expand Down Expand Up @@ -143,6 +171,23 @@ public static Tuple<double, double, double> HsvToHsl(double h, double s, double
return new Tuple<double, double, double>(h, hsl_s, hsl_l);
}

/// <summary>
/// Converts HSV to Grayscale
/// </summary>
/// <param name="h">Hue, 0-360</param>
/// <param name="s">Saturation, 0-1</param>
/// <param name="v">Value, 0-1</param>
/// <returns>Values (0-1) in order: R, G, B (all same)</returns>
public static Tuple<double, double, double> 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<double, double, double>(grey, grey, grey);
}

/// <summary>
/// Converts HSL to RGB
/// </summary>
Expand Down Expand Up @@ -199,5 +244,24 @@ public static Tuple<double, double, double> HslToHsv(double h, double s, double
hsv_s = 2 * (1 - l / hsv_v);
return new Tuple<double, double, double>(h, hsv_s, hsv_v);
}

/// <summary>
/// Converts HSL to Grayscale
/// </summary>
/// <remarks>This method uses the lightness component of the HSL color to calculate the grayscale
/// value, effectively ignoring the hue and saturation components.</remarks>
/// <param name="h">Hue, 0-360</param>
/// <param name="s">Saturation, 0-1</param>
/// <param name="l">Lightness, 0-1</param>
/// <returns>Values (0-1) in order: R, G, B (all same)</returns>
public static Tuple<double, double, double> 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<double, double, double>(gray, gray, gray);
}
}
}
66 changes: 51 additions & 15 deletions src/ColorPicker/DualPickerControlBase.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand Down Expand Up @@ -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; }
Expand Down
30 changes: 28 additions & 2 deletions src/ColorPicker/PickerControlBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,40 @@ public PickerControlBase()
previousColor = newColor;
}
};
ColorChanged += (sender, newColor) =>

var updateColorAction = new Action<object, RoutedEventArgs>
((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));
};
}

Expand Down
23 changes: 16 additions & 7 deletions src/ColorPicker/UIExtensions/HslColorSlider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<double, double, double>(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));
}
}
}
Expand Down
23 changes: 17 additions & 6 deletions src/ColorPicker/UIExtensions/HsvColorSlider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Windows;
using System;
using System.Windows;
using System.Windows.Media;
using ColorPicker.Models;

Expand Down Expand Up @@ -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<double, double, double>(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));
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/ColorPicker/UIExtensions/PreviewColorSlider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public PreviewColorSlider()
LargeChange = 10;
MinHeight = 12;
PreviewMouseWheel += OnPreviewMouseWheel;
IsEnabledChanged += (s, e) =>
{
GenerateBackground();
};
}

protected virtual bool RefreshGradient => true;
Expand Down
11 changes: 6 additions & 5 deletions src/ColorPicker/UIExtensions/RgbColorSlider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

;
Expand Down
18 changes: 17 additions & 1 deletion src/ColorPicker/UserControls/HueSlider.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@
mc:Ignorable="d"
x:ClassModifier="internal"
x:Name="uc">
<UserControl.Resources>
<BitmapImage x:Key="masterImage" UriSource="/ColorPicker;component/Images/CircularHueGradient.png" />
<FormatConvertedBitmap x:Key="grayscaleImage"
Source="{StaticResource masterImage}"
DestinationFormat="Gray32Float" />
</UserControl.Resources>
<Viewbox>
<Grid>
<Image Source="/ColorPicker;component/Images/CircularHueGradient.png" Stretch="Fill"
<Image Stretch="Fill"
IsHitTestVisible="False">
<Image.OpacityMask>
<RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5" RadiusX="0.5" RadiusY="0.5">
Expand All @@ -21,6 +27,16 @@
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</Image.OpacityMask>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="{StaticResource masterImage}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=uc, Path=IsEnabled}" Value="False">
<Setter Property="Source" Value="{StaticResource grayscaleImage}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<Ellipse HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Stroke="#aa000000" StrokeThickness="2" Width="260" Height="260" IsHitTestVisible="False" />
Expand Down
Loading