Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e511169
Added clamping on properties in HsvColor and HslColor
Avid29 Oct 21, 2025
39cc112
Moved HSL and HSV to RGB logic into HslColor and HsvColor
Avid29 Oct 21, 2025
6ca048f
Moved HSL and HSV from RGB logic into HslColor and HsvColor
Avid29 Oct 21, 2025
d33117b
Marked final obsolete functions and split ColorHelper with ColorExten…
Avid29 Oct 21, 2025
88decd4
Added ToString methods on HsvColor and HslColor, and explicit cast be…
Avid29 Oct 21, 2025
0a2ce89
Replaced deprecated API usage with new API usage in ColorHelper tests
Avid29 Oct 21, 2025
2bdfcd2
Split ParseColor into various child methods for different formats
Avid29 Oct 22, 2025
0ae3f29
Renamed ColorHelper tests to match new API
Avid29 Oct 23, 2025
9e45d23
Added hsl and hsv color parsing
Avid29 Oct 23, 2025
6fbddcf
Restored public fields with obsolete attributes to remove breaking ch…
Avid29 Oct 24, 2025
6f93d65
Changed HslColor and HsvColor not to use deprecated fields internally
Avid29 Nov 18, 2025
08b2cd7
Added static Color extensions with #if NET10 clause
Avid29 Nov 18, 2025
384c9f4
Merge branch 'CommunityToolkit:main' into Color-Helper
Avid29 Nov 18, 2025
63a4a4f
Added Never EditorBrowsable attributes to deprecated fields to furthe…
Avid29 Nov 18, 2025
4b76529
Added AlphaOver and Mix color extension methods
Avid29 Nov 26, 2025
e20c31f
Added H/S/V/L, Add, and Subtract methods
Avid29 Nov 27, 2025
bf2fa1d
Added test code for new ColorExtension methods
Avid29 Nov 27, 2025
157d833
Renamed @base to original in WithX methods
Avid29 Nov 27, 2025
f115669
Added depreciation warning suppression in ColorHelper tests
Avid29 Nov 27, 2025
da578c9
Added static ColorHelper methods to ColorExtensions NET10 API
Avid29 Nov 27, 2025
25a9139
Updated ColorPicker to use new ColorHelper APIs because the warnings …
Avid29 Nov 27, 2025
8393fc6
Moved Color.Mix test into NET10 API block
Avid29 Nov 27, 2025
db7f6d8
Changed HslColor and HsvColor constructors to use fields. This fixes …
Avid29 Nov 27, 2025
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
63 changes: 26 additions & 37 deletions components/ColorPicker/src/ColorPicker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -510,12 +510,12 @@ private void UpdateColorControlValues()
if (this.IsAlphaEnabled)
{
// Remove only the "#" sign
this.HexInputTextBox.Text = rgbColor.ToHex().Replace("#", string.Empty);
this.HexInputTextBox.Text = rgbColor.ToString().TrimStart('#');
}
else
{
// Remove the "#" sign and alpha hex
this.HexInputTextBox.Text = rgbColor.ToHex().Replace("#", string.Empty).Substring(2);
this.HexInputTextBox.Text = rgbColor.ToString().TrimStart('#')[2..];
}
}

Expand All @@ -532,10 +532,10 @@ private void UpdateColorControlValues()
int decimals = 0;
hsvColor = new HsvColor()
{
H = Math.Round(hsvColor.H, decimals),
S = Math.Round(hsvColor.S, 2 + decimals),
V = Math.Round(hsvColor.V, 2 + decimals),
A = Math.Round(hsvColor.A, 2 + decimals)
Hue = Math.Round(hsvColor.Hue, decimals),
Saturation = Math.Round(hsvColor.Saturation, 2 + decimals),
Value = Math.Round(hsvColor.Value, 2 + decimals),
Alpha = Math.Round(hsvColor.Alpha, 2 + decimals)
};

// Must update HSV color
Expand All @@ -554,20 +554,20 @@ private void UpdateColorControlValues()
{
this.ColorSpectrumControl.HsvColor = new System.Numerics.Vector4()
{
X = Convert.ToSingle(hsvColor.H),
Y = Convert.ToSingle(hsvColor.S),
Z = Convert.ToSingle(hsvColor.V),
W = Convert.ToSingle(hsvColor.A)
X = Convert.ToSingle(hsvColor.Hue),
Y = Convert.ToSingle(hsvColor.Saturation),
Z = Convert.ToSingle(hsvColor.Value),
W = Convert.ToSingle(hsvColor.Alpha)
};
}

// Update the color spectrum third dimension channel
if (this.ColorSpectrumThirdDimensionSlider != null)
{
// Convert the channels into a usable range for the user
double hue = hsvColor.H;
double staturation = hsvColor.S * 100;
double value = hsvColor.V * 100;
double hue = hsvColor.Hue;
double staturation = hsvColor.Saturation * 100;
double value = hsvColor.Value * 100;

switch (this.GetActiveColorSpectrumThirdDimension())
{
Expand Down Expand Up @@ -610,10 +610,10 @@ private void UpdateColorControlValues()
if (this.GetActiveColorRepresentation() == ColorRepresentation.Hsva)
{
// Convert the channels into a usable range for the user
double hue = hsvColor.H;
double staturation = hsvColor.S * 100;
double value = hsvColor.V * 100;
double alpha = hsvColor.A * 100;
double hue = hsvColor.Hue;
double staturation = hsvColor.Saturation * 100;
double value = hsvColor.Value * 100;
double alpha = hsvColor.Alpha * 100;

// Hue
if (this.Channel1NumberBox != null)
Expand Down Expand Up @@ -792,10 +792,10 @@ private void SetColorChannel(
oldHsvColor = this.savedHsvColor.Value;
}

double hue = oldHsvColor.H;
double saturation = oldHsvColor.S;
double value = oldHsvColor.V;
double alpha = oldHsvColor.A;
double hue = oldHsvColor.Hue;
double saturation = oldHsvColor.Saturation;
double value = oldHsvColor.Value;
double alpha = oldHsvColor.Alpha;

switch (channel)
{
Expand Down Expand Up @@ -825,20 +825,11 @@ private void SetColorChannel(
}
}

newRgbColor = Helpers.ColorHelper.FromHsv(
hue,
saturation,
value,
alpha);
var hsv = HsvColor.Create(hue, saturation, value, alpha);
newRgbColor = hsv;

// Must update HSV color
this.savedHsvColor = new HsvColor()
{
H = hue,
S = saturation,
V = value,
A = alpha
};
this.savedHsvColor = hsv;
this.savedHsvColorRgbEquivalent = newRgbColor;
}
else
Expand Down Expand Up @@ -1351,16 +1342,14 @@ private void ColorModeComboBox_SelectionChanged(object sender, SelectionChangedE
/// </summary>
private void ColorPreviewer_ColorChangeRequested(object? sender, HsvColor hsvColor)
{
Color rgbColor = Helpers.ColorHelper.FromHsv(hsvColor.H, hsvColor.S, hsvColor.V, hsvColor.A);

// Regardless of the active color model, the previewer always uses HSV
// Therefore, always calculate HSV color here
// Warning: Always maintain/use HSV information in the saved HSV color
// This avoids loss of precision and drift caused by continuously converting to/from RGB
this.savedHsvColor = hsvColor;
this.savedHsvColorRgbEquivalent = rgbColor;
this.savedHsvColorRgbEquivalent = hsvColor;

this.ScheduleColorUpdate(rgbColor);
this.ScheduleColorUpdate(hsvColor);

return;
}
Expand Down
72 changes: 19 additions & 53 deletions components/ColorPicker/src/ColorPickerRenderingHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,23 +79,13 @@ public static async Task<byte[]> CreateChannelBitmapAsync(
if (isAlphaMaxForced &&
channel != ColorChannel.Alpha)
{
baseHsvColor = new HsvColor()
{
H = baseHsvColor.H,
S = baseHsvColor.S,
V = baseHsvColor.V,
A = 1.0
};
baseHsvColor.Alpha = 1.0;
}

// Convert HSV to RGB once
if (colorRepresentation == ColorRepresentation.Rgba)
{
baseRgbColor = Helpers.ColorHelper.FromHsv(
baseHsvColor.H,
baseHsvColor.S,
baseHsvColor.V,
baseHsvColor.A);
baseRgbColor = baseHsvColor;
}

// Maximize Saturation and Value channels when in HSVA mode
Expand All @@ -106,31 +96,15 @@ public static async Task<byte[]> CreateChannelBitmapAsync(
switch (channel)
{
case ColorChannel.Channel1:
baseHsvColor = new HsvColor()
{
H = baseHsvColor.H,
S = 1.0,
V = 1.0,
A = baseHsvColor.A
};
baseHsvColor.Saturation = 1.0;
baseHsvColor.Value = 1.0;
break;
case ColorChannel.Channel2:
baseHsvColor = new HsvColor()
{
H = baseHsvColor.H,
S = baseHsvColor.S,
V = 1.0,
A = baseHsvColor.A
};

baseHsvColor.Value = 1;
break;
case ColorChannel.Channel3:
baseHsvColor = new HsvColor()
{
H = baseHsvColor.H,
S = 1.0,
V = baseHsvColor.V,
A = baseHsvColor.A
};
baseHsvColor.Saturation = 1;
break;
}
}
Expand Down Expand Up @@ -300,11 +274,9 @@ Color GetColor(double channelValue)
if (colorRepresentation == ColorRepresentation.Hsva)
{
// Sweep hue
newRgbColor = Helpers.ColorHelper.FromHsv(
Math.Clamp(channelValue, 0.0, 360.0),
baseHsvColor.S,
baseHsvColor.V,
baseHsvColor.A);
var hsv = baseHsvColor;
hsv.Hue = channelValue;
newRgbColor = hsv;
}
else
{
Expand All @@ -326,11 +298,9 @@ Color GetColor(double channelValue)
if (colorRepresentation == ColorRepresentation.Hsva)
{
// Sweep saturation
newRgbColor = Helpers.ColorHelper.FromHsv(
baseHsvColor.H,
Math.Clamp(channelValue, 0.0, 1.0),
baseHsvColor.V,
baseHsvColor.A);
var hsv = baseHsvColor;
hsv.Saturation = channelValue;
newRgbColor = hsv;
}
else
{
Expand All @@ -352,11 +322,9 @@ Color GetColor(double channelValue)
if (colorRepresentation == ColorRepresentation.Hsva)
{
// Sweep value
newRgbColor = Helpers.ColorHelper.FromHsv(
baseHsvColor.H,
baseHsvColor.S,
Math.Clamp(channelValue, 0.0, 1.0),
baseHsvColor.A);
var hsv = baseHsvColor;
hsv.Value = channelValue;
newRgbColor = hsv;
}
else
{
Expand All @@ -378,11 +346,9 @@ Color GetColor(double channelValue)
if (colorRepresentation == ColorRepresentation.Hsva)
{
// Sweep alpha
newRgbColor = Helpers.ColorHelper.FromHsv(
baseHsvColor.H,
baseHsvColor.S,
baseHsvColor.V,
Math.Clamp(channelValue, 0.0, 1.0));
var hsv = baseHsvColor;
hsv.Alpha = channelValue;
newRgbColor = hsv;
}
else
{
Expand Down
50 changes: 16 additions & 34 deletions components/ColorPicker/src/ColorPickerSlider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public void UpdateColors()
this.UpdateBackground(hsvColor);

// Calculate and set the foreground ensuring contrast with the background
Color rgbColor = Helpers.ColorHelper.FromHsv(hsvColor.H, hsvColor.S, hsvColor.V, hsvColor.A);
Color rgbColor = hsvColor;
Color selectedRgbColor;
double sliderPercent = this.Value / (this.Maximum - this.Minimum);

Expand All @@ -64,65 +64,47 @@ public void UpdateColors()
if (this.IsAlphaMaxForced &&
this.ColorChannel != ColorChannel.Alpha)
{
hsvColor = new HsvColor()
{
H = hsvColor.H,
S = hsvColor.S,
V = hsvColor.V,
A = 1.0
};
hsvColor.Alpha = 1.0;
}

switch (this.ColorChannel)
{
case ColorChannel.Channel1:
{
var channelValue = Math.Clamp(sliderPercent * 360.0, 0.0, 360.0);

hsvColor = new HsvColor()
hsvColor.Hue = channelValue;
if (this.IsSaturationValueMaxForced)
{
H = channelValue,
S = this.IsSaturationValueMaxForced ? 1.0 : hsvColor.S,
V = this.IsSaturationValueMaxForced ? 1.0 : hsvColor.V,
A = hsvColor.A
};
hsvColor.Saturation = 1.0;
hsvColor.Value = 1.0;
}
break;
}

case ColorChannel.Channel2:
{
var channelValue = Math.Clamp(sliderPercent * 1.0, 0.0, 1.0);

hsvColor = new HsvColor()
hsvColor.Saturation = channelValue;
if (this.IsSaturationValueMaxForced)
{
H = hsvColor.H,
S = channelValue,
V = this.IsSaturationValueMaxForced ? 1.0 : hsvColor.V,
A = hsvColor.A
};
hsvColor.Value = 1.0;
}
break;
}

case ColorChannel.Channel3:
{
var channelValue = Math.Clamp(sliderPercent * 1.0, 0.0, 1.0);

hsvColor = new HsvColor()
hsvColor.Value = channelValue;
if (this.IsSaturationValueMaxForced)
{
H = hsvColor.H,
S = this.IsSaturationValueMaxForced ? 1.0 : hsvColor.S,
V = channelValue,
A = hsvColor.A
};
hsvColor.Saturation = 1.0;
}
break;
}
}

selectedRgbColor = Helpers.ColorHelper.FromHsv(
hsvColor.H,
hsvColor.S,
hsvColor.V,
hsvColor.A);
selectedRgbColor = hsvColor;
}
else
{
Expand Down
Loading
Loading