Merge pull request #7555 from TobiasSekan/MoreColorsTakeTwo

[ColorPicker] Add HSB, HSI, HWB and NCol color representation
This commit is contained in:
Clint Rutkas 2020-11-09 15:16:32 -08:00 committed by GitHub
commit 2d61b69ff5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 589 additions and 228 deletions

View File

@ -295,6 +295,7 @@ codeofconduct
codereview codereview
COINIT COINIT
Colorbrush Colorbrush
colorconv
colorpicker colorpicker
colorpickerref colorpickerref
COLORREF COLORREF
@ -903,6 +904,8 @@ hresult
hrgn hrgn
HRSRC HRSRC
HSCROLL HSCROLL
hsb
hsi
hsl hsl
hstring hstring
hsv hsv
@ -913,6 +916,7 @@ html
htt htt
http http
hu hu
hwb
HWINEVENTHOOK HWINEVENTHOOK
hwnd hwnd
HWNDFIRST HWNDFIRST
@ -1087,6 +1091,7 @@ IPrincipal
IProgram IProgram
IPublic IPublic
IQuery IQuery
IRead
IReflect IReflect
IRegistered IRegistered
IRegistration IRegistration
@ -1439,6 +1444,7 @@ NCMBUTTONDOWN
NCMBUTTONUP NCMBUTTONUP
NCMOUSELEAVE NCMOUSELEAVE
NCMOUSEMOVE NCMOUSEMOVE
NCol
NCPAINT NCPAINT
NCRBUTTONDBLCLK NCRBUTTONDBLCLK
NCRBUTTONDOWN NCRBUTTONDOWN

View File

@ -10,6 +10,11 @@ data:[a-zA-Z=;,/0-9+-]+
"Lorem[^"]+?\." "Lorem[^"]+?\."
TestCase\("[^"]+" TestCase\("[^"]+"
# Test line with hexadecimal colors
\[DataRow\("[0-9A-F]{6}", \d{3}, \d{3}, \d{3}\)\]
\[DataRow\("[0-9A-F]{6}", \d{3}.\d{1}, \d{3}.\d{1}, \d{3}.\d{1}\)\]
\[DataRow\("[0-9A-F]{6}", "[BCGMRY]\d\d?", \d{3}, \d{3}\)\]
# Windows paths # Windows paths
\\native \\native
\\notifications \\notifications

View File

@ -4,37 +4,10 @@
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
namespace Microsoft.PowerToys.Settings.UI.Library namespace Microsoft.PowerToys.Settings.UI.Library
{ {
public enum ColorRepresentationType
{
/// <summary>
/// Color presentation as hexadecimal color value without the alpha-value (e.g. #0055FF)
/// </summary>
HEX = 0,
/// <summary>
/// Color presentation as RGB color value (red[0..255], green[0..255], blue[0..255])
/// </summary>
RGB = 1,
/// <summary>
/// Color presentation as CMYK color value (cyan[0%..100%], magenta[0%..100%], yellow[0%..100%], black key[0%..100%])
/// </summary>
CMYK = 2,
/// <summary>
/// Color presentation as HSL color value (hue[0°..360°], saturation[0..100%], lightness[0%..100%])
/// </summary>
HSL = 3,
/// <summary>
/// Color presentation as HSV color value (hue[0°..360°], saturation[0%..100%], value[0%..100%])
/// </summary>
HSV = 4,
}
public class ColorPickerProperties public class ColorPickerProperties
{ {
public ColorPickerProperties() public ColorPickerProperties()

View File

@ -40,14 +40,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
} }
public string GetModuleName() public string GetModuleName()
{ => Name;
return Name;
}
// This can be utilized in the future if the settings.json file is to be modified/deleted. // This can be utilized in the future if the settings.json file is to be modified/deleted.
public bool UpgradeSettingsConfiguration() public bool UpgradeSettingsConfiguration()
{ => false;
return false;
}
} }
} }

View File

@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.PowerToys.Settings.UI.Library.Enumerations
{
// NOTE: don't change the order (numbers) of the enumeration entires
/// <summary>
/// The type of the color representation
/// </summary>
public enum ColorRepresentationType
{
/// <summary>
/// Color presentation as hexadecimal color value without the alpha-value (e.g. #0055FF)
/// </summary>
HEX = 0,
/// <summary>
/// Color presentation as RGB color value (red[0..255], green[0..255], blue[0..255])
/// </summary>
RGB = 1,
/// <summary>
/// Color presentation as CMYK color value (cyan[0%..100%], magenta[0%..100%], yellow[0%..100%], black key[0%..100%])
/// </summary>
CMYK = 2,
/// <summary>
/// Color presentation as HSL color value (hue[0°..360°], saturation[0..100%], lightness[0%..100%])
/// </summary>
HSL = 3,
/// <summary>
/// Color presentation as HSV color value (hue[0°..360°], saturation[0%..100%], value[0%..100%])
/// </summary>
HSV = 4,
/// <summary>
/// Color presentation as HSB color value (hue[0°..360°], saturation[0%..100%], brightness[0%..100%])
/// </summary>
HSB = 5,
/// <summary>
/// Color presentation as HSI color value (hue[0°..360°], saturation[0%..100%], intensity[0%..100%])
/// </summary>
HSI = 6,
/// <summary>
/// Color presentation as HWB color value (hue[0°..360°], whiteness[0%..100%], blackness[0%..100%])
/// </summary>
HWB = 7,
/// <summary>
/// Color presentation as natural color (hue, whiteness[0%..100%], blackness[0%..100%])
/// </summary>
NCol = 8,
}
}

View File

@ -3,8 +3,10 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Text.Json; using System.Text.Json;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
@ -16,7 +18,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
private readonly ISettingsUtils _settingsUtils; private readonly ISettingsUtils _settingsUtils;
private ColorPickerSettings _colorPickerSettings; private readonly ColorPickerSettings _colorPickerSettings;
private bool _isEnabled; private bool _isEnabled;
@ -30,6 +32,19 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
throw new ArgumentNullException(nameof(settingsRepository)); throw new ArgumentNullException(nameof(settingsRepository));
} }
SelectableColorRepresentations = new Dictionary<ColorRepresentationType, string>
{
{ ColorRepresentationType.CMYK, "CMYK - cmyk(100%, 50%, 75%, 0%)" },
{ ColorRepresentationType.HEX, "HEX - #FFAA00" },
{ ColorRepresentationType.HSB, "HSB - hsb(100, 50%, 75%)" },
{ ColorRepresentationType.HSI, "HSI - hsi(100, 50%, 75%)" },
{ ColorRepresentationType.HSL, "HSL - hsl(100, 50%, 75%)" },
{ ColorRepresentationType.HSV, "HSV - hsv(100, 50%, 75%)" },
{ ColorRepresentationType.HWB, "HWB - hwb(100, 50%, 75%)" },
{ ColorRepresentationType.NCol, "NCol - R10, 50%, 75%" },
{ ColorRepresentationType.RGB, "RGB - rgb(100, 50, 75)" },
};
GeneralSettingsConfig = settingsRepository.SettingsConfig; GeneralSettingsConfig = settingsRepository.SettingsConfig;
_settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils));
@ -48,13 +63,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
SendConfigMSG = ipcMSGCallBackFunc; SendConfigMSG = ipcMSGCallBackFunc;
} }
/// <summary>
/// Gets a list with all selectable <see cref="ColorRepresentationType"/>s
/// </summary>
public IReadOnlyDictionary<ColorRepresentationType, string> SelectableColorRepresentations { get; }
public bool IsEnabled public bool IsEnabled
{ {
get get => _isEnabled;
{
return _isEnabled;
}
set set
{ {
if (_isEnabled != value) if (_isEnabled != value)
@ -64,7 +80,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
// Set the status of ColorPicker in the general settings // Set the status of ColorPicker in the general settings
GeneralSettingsConfig.Enabled.ColorPicker = value; GeneralSettingsConfig.Enabled.ColorPicker = value;
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig); var outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
SendConfigMSG(outgoing.ToString()); SendConfigMSG(outgoing.ToString());
} }
@ -73,11 +89,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
public bool ChangeCursor public bool ChangeCursor
{ {
get get => _colorPickerSettings.Properties.ChangeCursor;
{
return _colorPickerSettings.Properties.ChangeCursor;
}
set set
{ {
if (_colorPickerSettings.Properties.ChangeCursor != value) if (_colorPickerSettings.Properties.ChangeCursor != value)
@ -91,11 +103,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
public HotkeySettings ActivationShortcut public HotkeySettings ActivationShortcut
{ {
get get => _colorPickerSettings.Properties.ActivationShortcut;
{
return _colorPickerSettings.Properties.ActivationShortcut;
}
set set
{ {
if (_colorPickerSettings.Properties.ActivationShortcut != value) if (_colorPickerSettings.Properties.ActivationShortcut != value)
@ -107,19 +115,15 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
} }
} }
public int CopiedColorRepresentationIndex public ColorRepresentationType SelectedColorRepresentationValue
{ {
get get => _colorPickerSettings.Properties.CopiedColorRepresentation;
{
return (int)_colorPickerSettings.Properties.CopiedColorRepresentation;
}
set set
{ {
if (_colorPickerSettings.Properties.CopiedColorRepresentation != (ColorRepresentationType)value) if (_colorPickerSettings.Properties.CopiedColorRepresentation != value)
{ {
_colorPickerSettings.Properties.CopiedColorRepresentation = (ColorRepresentationType)value; _colorPickerSettings.Properties.CopiedColorRepresentation = value;
OnPropertyChanged(nameof(CopiedColorRepresentationIndex)); OnPropertyChanged(nameof(SelectedColorRepresentationValue));
NotifySettingsChanged(); NotifySettingsChanged();
} }
} }

View File

@ -13,68 +13,41 @@
AutomationProperties.LandmarkType="Main"> AutomationProperties.LandmarkType="Main">
<Grid RowSpacing="{StaticResource DefaultRowSpacing}"> <Grid RowSpacing="{StaticResource DefaultRowSpacing}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="LayoutVisualStates">
<VisualState x:Name="WideLayout">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource WideLayoutMinWidth}" />
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="SmallLayout">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource SmallLayoutMinWidth}" />
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="SidePanel.(Grid.Column)" Value="0"/>
<Setter Target="SidePanel.Width" Value="Auto"/>
<Setter Target="ColorPickerView.(Grid.Row)" Value="1" />
<Setter Target="LinksPanel.(RelativePanel.RightOf)" Value="AboutImage"/>
<Setter Target="LinksPanel.(RelativePanel.AlignTopWith)" Value="AboutImage"/>
<Setter Target="AboutImage.Margin" Value="0,12,12,0"/>
<Setter Target="AboutTitle.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<StackPanel Orientation="Vertical" x:Name="ColorPickerView"> <StackPanel x:Name="ColorPickerView" Orientation="Vertical">
<ToggleSwitch x:Uid="ColorPicker_EnableColorPicker" <ToggleSwitch x:Uid="ColorPicker_EnableColorPicker" IsOn="{Binding IsEnabled, Mode=TwoWay}" />
IsOn="{Binding IsEnabled, Mode=TwoWay}"/>
<Custom:HotkeySettingsControl x:Uid="ColorPicker_ActivationShortcut" <Custom:HotkeySettingsControl x:Uid="ColorPicker_ActivationShortcut"
Margin="{StaticResource MediumTopMargin}"
HotkeySettings="{x:Bind Path=ViewModel.ActivationShortcut, Mode=TwoWay}"
Keys="Win, Ctrl, Alt, Shift"
Enabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}"
HorizontalAlignment="Left"
MinWidth="240" MinWidth="240"
/> Margin="{StaticResource MediumTopMargin}"
HorizontalAlignment="Left"
Enabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}"
HotkeySettings="{x:Bind Path=ViewModel.ActivationShortcut, Mode=TwoWay}"
Keys="Win, Ctrl, Alt, Shift" />
<TextBlock x:Uid="ShortcutGuide_Appearance_Behavior" <TextBlock x:Uid="ShortcutGuide_Appearance_Behavior"
Style="{StaticResource SettingsGroupTitleStyle}" Foreground="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled, Converter={StaticResource ModuleEnabledToForegroundConverter}}"
Foreground="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled, Converter={StaticResource ModuleEnabledToForegroundConverter}}"/> Style="{StaticResource SettingsGroupTitleStyle}" />
<ComboBox x:Uid="ColorPicker_CopiedColorRepresentation" <ComboBox x:Name="ColorPicker_ComboBox"
SelectedIndex="{Binding CopiedColorRepresentationIndex, Mode=TwoWay}" x:Uid="ColorPicker_CopiedColorRepresentation"
MinWidth="240"
Margin="{StaticResource SmallTopMargin}" Margin="{StaticResource SmallTopMargin}"
IsEnabled="{Binding IsEnabled}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
MinWidth="240"> DisplayMemberPath="Value"
<ComboBoxItem Content="HEX - #FFAA00"/> IsEnabled="{Binding IsEnabled}"
<ComboBoxItem Content="RGB - rgb(100, 50, 75)"/> ItemsSource="{Binding SelectableColorRepresentations}"
<ComboBoxItem Content="CMYK - cmyk(100%, 50%, 75%, 0%)"/> Loaded="ColorPicker_ComboBox_Loaded"
<ComboBoxItem Content="HSL - hsl(100, 50%, 75%)"/> SelectedValue="{Binding SelectedColorRepresentationValue, Mode=TwoWay}"
<ComboBoxItem Content="HSV - hsv(100, 50%, 75%)"/> SelectedValuePath="Key" />
</ComboBox>
<!-- <!--
Disabling this until we have a safer way to reset cursor as Disabling this until we have a safer way to reset cursor as
@ -88,31 +61,33 @@
</StackPanel> </StackPanel>
<RelativePanel x:Name="SidePanel" <RelativePanel x:Name="SidePanel"
HorizontalAlignment="Left" Grid.Column="1"
Width="{StaticResource SidePanelWidth}" Width="{StaticResource SidePanelWidth}"
Grid.Column="1"> HorizontalAlignment="Left">
<StackPanel x:Name="DescriptionPanel"> <StackPanel x:Name="DescriptionPanel">
<TextBlock x:Uid="About_ColorPicker" x:Name="AboutTitle" Grid.ColumnSpan="2" <TextBlock x:Name="AboutTitle"
Style="{StaticResource SettingsGroupTitleStyle}" x:Uid="About_ColorPicker"
Margin="{StaticResource XSmallBottomMargin}"/> Grid.ColumnSpan="2"
Margin="{StaticResource XSmallBottomMargin}"
Style="{StaticResource SettingsGroupTitleStyle}" />
<TextBlock x:Uid="ColorPicker_Description" <TextBlock x:Uid="ColorPicker_Description"
TextWrapping="Wrap" Grid.Row="1"
Grid.Row="1" /> TextWrapping="Wrap" />
</StackPanel> </StackPanel>
<Border x:Name="AboutImage" <Border x:Name="AboutImage"
CornerRadius="4"
Grid.Row="2" Grid.Row="2"
MaxWidth="240" MaxWidth="240"
HorizontalAlignment="Left"
Margin="{StaticResource SmallTopBottomMargin}" Margin="{StaticResource SmallTopBottomMargin}"
HorizontalAlignment="Left"
CornerRadius="4"
RelativePanel.Below="DescriptionPanel"> RelativePanel.Below="DescriptionPanel">
<Image x:Uid="ColorPicker_Image" Source="ms-appx:///Assets/Modules/ColorPicker.png" /> <Image x:Uid="ColorPicker_Image" Source="ms-appx:///Assets/Modules/ColorPicker.png" />
</Border> </Border>
<StackPanel x:Name="LinksPanel" <StackPanel x:Name="LinksPanel"
Margin="0,1,0,0" Margin="0,1,0,0"
RelativePanel.Below="AboutImage" Orientation="Vertical"
Orientation="Vertical" > RelativePanel.Below="AboutImage">
<HyperlinkButton NavigateUri="https://aka.ms/PowerToysOverview_ColorPicker"> <HyperlinkButton NavigateUri="https://aka.ms/PowerToysOverview_ColorPicker">
<TextBlock x:Uid="Module_overview" /> <TextBlock x:Uid="Module_overview" />
</HyperlinkButton> </HyperlinkButton>
@ -120,15 +95,36 @@
<TextBlock x:Uid="Give_Feedback" /> <TextBlock x:Uid="Give_Feedback" />
</HyperlinkButton> </HyperlinkButton>
<TextBlock <TextBlock x:Uid="AttributionTitle" Style="{StaticResource SettingsGroupTitleStyle}" />
x:Uid="AttributionTitle"
Style="{StaticResource SettingsGroupTitleStyle}" />
<HyperlinkButton Margin="0,-3,0,0" <HyperlinkButton Margin="0,-3,0,0" NavigateUri="https://github.com/martinchrzan/ColorPicker/">
NavigateUri="https://github.com/martinchrzan/ColorPicker/">
<TextBlock Text="Martin Chrzan's Color Picker" TextWrapping="Wrap" /> <TextBlock Text="Martin Chrzan's Color Picker" TextWrapping="Wrap" />
</HyperlinkButton> </HyperlinkButton>
</StackPanel> </StackPanel>
</RelativePanel> </RelativePanel>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="LayoutVisualStates">
<VisualState x:Name="WideLayout">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource WideLayoutMinWidth}" />
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="SmallLayout">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource SmallLayoutMinWidth}" />
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="SidePanel.(Grid.Column)" Value="0" />
<Setter Target="SidePanel.Width" Value="Auto" />
<Setter Target="ColorPickerView.(Grid.Row)" Value="1" />
<Setter Target="LinksPanel.(RelativePanel.RightOf)" Value="AboutImage" />
<Setter Target="LinksPanel.(RelativePanel.AlignTopWith)" Value="AboutImage" />
<Setter Target="AboutImage.Margin" Value="0,12,12,0" />
<Setter Target="AboutTitle.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid> </Grid>
</Page> </Page>

View File

@ -21,5 +21,33 @@ namespace Microsoft.PowerToys.Settings.UI.Views
DataContext = ViewModel; DataContext = ViewModel;
InitializeComponent(); InitializeComponent();
} }
/// <summary>
/// Event is called when the <see cref="ComboBox"/> is completely loaded, inclusive the ItemSource
/// </summary>
/// <param name="sender">The sender of this event</param>
/// <param name="e">The arguments of this event</param>
private void ColorPicker_ComboBox_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
/**
* UWP hack
* because UWP load the bound ItemSource of the ComboBox asynchronous,
* so after InitializeComponent() the ItemSource is still empty and can't automatically select a entry.
* Selection via SelectedItem and SelectedValue is still not working too
*/
var index = 0;
foreach (var item in ViewModel.SelectableColorRepresentations)
{
if (item.Key == ViewModel.SelectedColorRepresentationValue)
{
break;
}
index++;
}
ColorPicker_ComboBox.SelectedIndex = index;
}
} }
} }

View File

@ -13,44 +13,7 @@ namespace ColorPicker.Helpers
internal static class ColorHelper internal static class ColorHelper
{ {
/// <summary> /// <summary>
/// Convert a given <see cref="Color"/> color to a HSL color (hue, saturation, lightness) /// Convert a given <see cref="Color"/> to a CYMK color (cyan, magenta, yellow, black key)
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert</param>
/// <returns>The hue [0°..360°], saturation [0..1] and lightness [0..1] values of the converted color</returns>
internal static (double hue, double saturation, double lightness) ConvertToHSLColor(Color color)
{
var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d;
var lightness = (max + min) / 2d;
if (lightness == 0d || min == max)
{
return (color.GetHue(), 0d, lightness);
}
else if (lightness > 0d && lightness <= 0.5d)
{
return (color.GetHue(), (max - min) / (max + min), lightness);
}
return (color.GetHue(), (max - min) / (2d - (max + min)), lightness);
}
/// <summary>
/// Convert a given <see cref="Color"/> color to a HSV color (hue, saturation, value)
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert</param>
/// <returns>The hue [0°..360°], saturation [0..1] and value [0..1] of the converted color</returns>
internal static (double hue, double saturation, double value) ConvertToHSVColor(Color color)
{
var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d;
return (color.GetHue(), max == 0d ? 0d : (max - min) / max, max);
}
/// <summary>
/// Convert a given <see cref="Color"/> color to a CYMK color (cyan, magenta, yellow, black key)
/// </summary> /// </summary>
/// <param name="color">The <see cref="Color"/> to convert</param> /// <param name="color">The <see cref="Color"/> to convert</param>
/// <returns>The cyan[0..1], magenta[0..1], yellow[0..1] and black key[0..1] of the converted color</returns> /// <returns>The cyan[0..1], magenta[0..1], yellow[0..1] and black key[0..1] of the converted color</returns>
@ -80,5 +43,135 @@ namespace ColorPicker.Helpers
return (cyan, magenta, yellow, blackKey); return (cyan, magenta, yellow, blackKey);
} }
/// <summary>
/// Convert a given <see cref="Color"/> to a HSB color (hue, saturation, brightness)
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert</param>
/// <returns>The hue [0°..360°], saturation [0..1] and brightness [0..1] of the converted color</returns>
internal static (double hue, double saturation, double brightness) ConvertToHSBColor(Color color)
=> (color.GetHue(), color.GetSaturation(), color.GetBrightness());
/// <summary>
/// Convert a given <see cref="Color"/> to a HSI color (hue, saturation, intensity)
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert</param>
/// <returns>The hue [0°..360°], saturation [0..1] and intensity [0..1] of the converted color</returns>
internal static (double hue, double saturation, double intensity) ConvertToHSIColor(Color color)
{
// special case for black
if (color.R == 0 && color.G == 0 && color.B == 0)
{
return (0d, 0d, 0d);
}
var red = color.R / 255d;
var green = color.G / 255d;
var blue = color.B / 255d;
var intensity = (red + green + blue) / 3d;
var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
return (color.GetHue(), 1d - (min / intensity), intensity);
}
/// <summary>
/// Convert a given <see cref="Color"/> to a HSL color (hue, saturation, lightness)
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert</param>
/// <returns>The hue [0°..360°], saturation [0..1] and lightness [0..1] values of the converted color</returns>
internal static (double hue, double saturation, double lightness) ConvertToHSLColor(Color color)
{
var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d;
var lightness = (max + min) / 2d;
if (lightness == 0d || min == max)
{
return (color.GetHue(), 0d, lightness);
}
else if (lightness > 0d && lightness <= 0.5d)
{
return (color.GetHue(), (max - min) / (max + min), lightness);
}
return (color.GetHue(), (max - min) / (2d - (max + min)), lightness);
}
/// <summary>
/// Convert a given <see cref="Color"/> to a HSV color (hue, saturation, value)
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert</param>
/// <returns>The hue [0°..360°], saturation [0..1] and value [0..1] of the converted color</returns>
internal static (double hue, double saturation, double value) ConvertToHSVColor(Color color)
{
var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d;
return (color.GetHue(), max == 0d ? 0d : (max - min) / max, max);
}
/// <summary>
/// Convert a given <see cref="Color"/> to a HWB color (hue, whiteness, blackness)
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert</param>
/// <returns>The hue [0°..360°], whiteness [0..1] and blackness [0..1] of the converted color</returns>
internal static (double hue, double whiteness, double blackness) ConvertToHWBColor(Color color)
{
var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d;
return (color.GetHue(), min, 1 - max);
}
/// <summary>
/// Convert a given <see cref="Color"/> to a natural color (hue, whiteness, blackness)
/// </summary>
/// <param name="color">The <see cref="Color"/> to convert</param>
/// <returns>The hue, whiteness [0..1] and blackness [0..1] of the converted color</returns>
internal static (string hue, double whiteness, double blackness) ConvertToNaturalColor(Color color)
{
var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d;
var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d;
return (GetNaturalColorFromHue(color.GetHue()), min, 1 - max);
}
/// <summary>
/// Return the natural color for the given hue value
/// </summary>
/// <param name="hue">The hue value to convert</param>
/// <returns>A natural color</returns>
private static string GetNaturalColorFromHue(double hue)
{
if (hue < 60d)
{
return $"R{Math.Round(hue / 0.6d, 0)}";
}
if (hue < 120d)
{
return $"Y{Math.Round((hue - 60d) / 0.6d, 0)}";
}
if (hue < 180d)
{
return $"G{Math.Round((hue - 120d) / 0.6d, 0)}";
}
if (hue < 240d)
{
return $"C{Math.Round((hue - 180d) / 0.6d, 0)}";
}
if (hue < 300d)
{
return $"B{Math.Round((hue - 240d) / 0.6d, 0)}";
}
return $"M{Math.Round((hue - 300d) / 0.6d, 0)}";
}
} }
} }

View File

@ -5,7 +5,7 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Globalization; using System.Globalization;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
namespace ColorPicker.Helpers namespace ColorPicker.Helpers
{ {
@ -25,14 +25,38 @@ namespace ColorPicker.Helpers
{ {
ColorRepresentationType.CMYK => ColorToCYMK(color), ColorRepresentationType.CMYK => ColorToCYMK(color),
ColorRepresentationType.HEX => ColorToHex(color), ColorRepresentationType.HEX => ColorToHex(color),
ColorRepresentationType.HSB => ColorToHSB(color),
ColorRepresentationType.HSI => ColorToHSI(color),
ColorRepresentationType.HSL => ColorToHSL(color), ColorRepresentationType.HSL => ColorToHSL(color),
ColorRepresentationType.HSV => ColorToHSV(color), ColorRepresentationType.HSV => ColorToHSV(color),
ColorRepresentationType.HWB => ColorToHWB(color),
ColorRepresentationType.NCol => ColorToNCol(color),
ColorRepresentationType.RGB => ColorToRGB(color), ColorRepresentationType.RGB => ColorToRGB(color),
// Fall-back value, when "_userSettings.CopiedColorRepresentation.Value" is incorrect // Fall-back value, when "_userSettings.CopiedColorRepresentation.Value" is incorrect
_ => ColorToHex(color), _ => ColorToHex(color),
}; };
/// <summary>
/// Return a <see cref="string"/> representation of a CYMK color
/// </summary>
/// <param name="color">The <see cref="Color"/> for the CYMK color presentation</param>
/// <returns>A <see cref="string"/> representation of a CYMK color</returns>
private static string ColorToCYMK(Color color)
{
var (cyan, magenta, yellow, blackKey) = ColorHelper.ConvertToCMYKColor(color);
cyan = Math.Round(cyan * 100);
magenta = Math.Round(magenta * 100);
yellow = Math.Round(yellow * 100);
blackKey = Math.Round(blackKey * 100);
return $"cmyk({cyan.ToString(CultureInfo.InvariantCulture)}%"
+ $", {magenta.ToString(CultureInfo.InvariantCulture)}%"
+ $", {yellow.ToString(CultureInfo.InvariantCulture)}%"
+ $", {blackKey.ToString(CultureInfo.InvariantCulture)}%)";
}
/// <summary> /// <summary>
/// Return a hexadecimal <see cref="string"/> representation of a RGB color /// Return a hexadecimal <see cref="string"/> representation of a RGB color
/// </summary> /// </summary>
@ -44,19 +68,45 @@ namespace ColorPicker.Helpers
+ $"{color.B.ToString("X2", CultureInfo.InvariantCulture)}"; + $"{color.B.ToString("X2", CultureInfo.InvariantCulture)}";
/// <summary> /// <summary>
/// Return a <see cref="string"/> representation of a RGB color /// Return a <see cref="string"/> representation of a HSB color
/// </summary> /// </summary>
/// <param name="color">The see cref="Color"/> for the RGB color presentation</param> /// <param name="color">The <see cref="Color"/> for the HSB color presentation</param>
/// <returns>A <see cref="string"/> representation of a RGB color</returns> /// <returns>A <see cref="string"/> representation of a HSB color</returns>
private static string ColorToRGB(Color color) private static string ColorToHSB(Color color)
=> $"rgb({color.R.ToString(CultureInfo.InvariantCulture)}" {
+ $", {color.G.ToString(CultureInfo.InvariantCulture)}" var (hue, saturation, brightness) = ColorHelper.ConvertToHSBColor(color);
+ $", {color.B.ToString(CultureInfo.InvariantCulture)})";
hue = Math.Round(hue);
saturation = Math.Round(saturation * 100);
brightness = Math.Round(brightness * 100);
return $"hsb({hue.ToString(CultureInfo.InvariantCulture)}"
+ $", {saturation.ToString(CultureInfo.InvariantCulture)}%"
+ $", {brightness.ToString(CultureInfo.InvariantCulture)}%)";
}
/// <summary>
/// Return a <see cref="string"/> representation of a HSI color
/// </summary>
/// <param name="color">The <see cref="Color"/> for the HSI color presentation</param>
/// <returns>A <see cref="string"/> representation of a HSI color</returns>
private static string ColorToHSI(Color color)
{
var (hue, saturation, intensity) = ColorHelper.ConvertToHSIColor(color);
hue = Math.Round(hue);
saturation = Math.Round(saturation * 100);
intensity = Math.Round(intensity * 100);
return $"hsi({hue.ToString(CultureInfo.InvariantCulture)}"
+ $", {saturation.ToString(CultureInfo.InvariantCulture)}%"
+ $", {intensity.ToString(CultureInfo.InvariantCulture)}%)";
}
/// <summary> /// <summary>
/// Return a <see cref="string"/> representation of a HSL color /// Return a <see cref="string"/> representation of a HSL color
/// </summary> /// </summary>
/// <param name="color">The see cref="Color"/> for the HSL color presentation</param> /// <param name="color">The <see cref="Color"/> for the HSL color presentation</param>
/// <returns>A <see cref="string"/> representation of a HSL color</returns> /// <returns>A <see cref="string"/> representation of a HSL color</returns>
private static string ColorToHSL(Color color) private static string ColorToHSL(Color color)
{ {
@ -75,7 +125,7 @@ namespace ColorPicker.Helpers
/// <summary> /// <summary>
/// Return a <see cref="string"/> representation of a HSV color /// Return a <see cref="string"/> representation of a HSV color
/// </summary> /// </summary>
/// <param name="color">The see cref="Color"/> for the HSV color presentation</param> /// <param name="color">The <see cref="Color"/> for the HSV color presentation</param>
/// <returns>A <see cref="string"/> representation of a HSV color</returns> /// <returns>A <see cref="string"/> representation of a HSV color</returns>
private static string ColorToHSV(Color color) private static string ColorToHSV(Color color)
{ {
@ -92,24 +142,48 @@ namespace ColorPicker.Helpers
} }
/// <summary> /// <summary>
/// Return a <see cref="string"/> representation of a HSV color /// Return a <see cref="string"/> representation of a HWB color
/// </summary> /// </summary>
/// <param name="color">The see cref="Color"/> for the HSV color presentation</param> /// <param name="color">The <see cref="Color"/> for the HWB color presentation</param>
/// <returns>A <see cref="string"/> representation of a HSV color</returns> /// <returns>A <see cref="string"/> representation of a HWB color</returns>
private static string ColorToCYMK(Color color) private static string ColorToHWB(Color color)
{ {
var (cyan, magenta, yellow, blackKey) = ColorHelper.ConvertToCMYKColor(color); var (hue, whiteness, blackness) = ColorHelper.ConvertToHWBColor(color);
cyan = Math.Round(cyan * 100); hue = Math.Round(hue);
magenta = Math.Round(magenta * 100); whiteness = Math.Round(whiteness * 100);
yellow = Math.Round(yellow * 100); blackness = Math.Round(blackness * 100);
blackKey = Math.Round(blackKey * 100);
// Using InvariantCulture since this is used for color representation return $"hwb({hue.ToString(CultureInfo.InvariantCulture)}"
return $"cmyk({cyan.ToString(CultureInfo.InvariantCulture)}%" + $", {whiteness.ToString(CultureInfo.InvariantCulture)}%"
+ $", {magenta.ToString(CultureInfo.InvariantCulture)}%" + $", {blackness.ToString(CultureInfo.InvariantCulture)}%)";
+ $", {yellow.ToString(CultureInfo.InvariantCulture)}%"
+ $", {blackKey.ToString(CultureInfo.InvariantCulture)}%)";
} }
/// <summary>
/// Return a <see cref="string"/> representation of a natural color
/// </summary>
/// <param name="color">The <see cref="Color"/> for the natural color presentation</param>
/// <returns>A <see cref="string"/> representation of a natural color</returns>
private static string ColorToNCol(Color color)
{
var (hue, whiteness, blackness) = ColorHelper.ConvertToNaturalColor(color);
whiteness = Math.Round(whiteness * 100);
blackness = Math.Round(blackness * 100);
return $"{hue}"
+ $", {whiteness.ToString(CultureInfo.InvariantCulture)}%"
+ $", {blackness.ToString(CultureInfo.InvariantCulture)}%";
}
/// <summary>
/// Return a <see cref="string"/> representation of a RGB color
/// </summary>
/// <param name="color">The see cref="Color"/> for the RGB color presentation</param>
/// <returns>A <see cref="string"/> representation of a RGB color</returns>
private static string ColorToRGB(Color color)
=> $"rgb({color.R.ToString(CultureInfo.InvariantCulture)}"
+ $", {color.G.ToString(CultureInfo.InvariantCulture)}"
+ $", {color.B.ToString(CultureInfo.InvariantCulture)})";
} }
} }

View File

@ -2,7 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
namespace ColorPicker.Settings namespace ColorPicker.Settings
{ {

View File

@ -8,6 +8,7 @@ using System.IO;
using System.IO.Abstractions; using System.IO.Abstractions;
using System.Threading; using System.Threading;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Library.Utilities;
namespace ColorPicker.Settings namespace ColorPicker.Settings

View File

@ -15,7 +15,6 @@ using ColorPicker.Mouse;
using ColorPicker.Settings; using ColorPicker.Settings;
using ColorPicker.Telemetry; using ColorPicker.Telemetry;
using ColorPicker.ViewModelContracts; using ColorPicker.ViewModelContracts;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry;
namespace ColorPicker.ViewModels namespace ColorPicker.ViewModels

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Globalization;
using ColorPicker.Helpers; using ColorPicker.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
@ -41,7 +42,7 @@ namespace UnitTest_ColorPickerUI.Helpers
[DataRow(315, 100, 050, 100, 000, 075)] // Red-magenta [DataRow(315, 100, 050, 100, 000, 075)] // Red-magenta
[DataRow(330, 100, 050, 100, 000, 050)] // Blue-red [DataRow(330, 100, 050, 100, 000, 050)] // Blue-red
[DataRow(345, 100, 050, 100, 000, 025)] // Light blue-red [DataRow(345, 100, 050, 100, 000, 025)] // Light blue-red
public void ColorRGBtoHSL(double hue, double saturation, double lightness, int red, int green, int blue) public void ColorRGBtoHSLTest(double hue, double saturation, double lightness, int red, int green, int blue)
{ {
red = Convert.ToInt32(Math.Round(255d / 100d * red)); // [0%..100%] to [0..255] red = Convert.ToInt32(Math.Round(255d / 100d * red)); // [0%..100%] to [0..255]
green = Convert.ToInt32(Math.Round(255d / 100d * green)); // [0%..100%] to [0..255] green = Convert.ToInt32(Math.Round(255d / 100d * green)); // [0%..100%] to [0..255]
@ -90,7 +91,7 @@ namespace UnitTest_ColorPickerUI.Helpers
[DataRow(315, 100, 100, 100, 000, 075)] // Red-magenta [DataRow(315, 100, 100, 100, 000, 075)] // Red-magenta
[DataRow(330, 100, 100, 100, 000, 050)] // Blue-red [DataRow(330, 100, 100, 100, 000, 050)] // Blue-red
[DataRow(345, 100, 100, 100, 000, 025)] // Light blue-red [DataRow(345, 100, 100, 100, 000, 025)] // Light blue-red
public void ColorRGBtoHSV(double hue, double saturation, double value, int red, int green, int blue) public void ColorRGBtoHSVTest(double hue, double saturation, double value, int red, int green, int blue)
{ {
red = Convert.ToInt32(Math.Round(255d / 100d * red)); // [0%..100%] to [0..255] red = Convert.ToInt32(Math.Round(255d / 100d * red)); // [0%..100%] to [0..255]
green = Convert.ToInt32(Math.Round(255d / 100d * green)); // [0%..100%] to [0..255] green = Convert.ToInt32(Math.Round(255d / 100d * green)); // [0%..100%] to [0..255]
@ -138,7 +139,7 @@ namespace UnitTest_ColorPickerUI.Helpers
[DataRow(000, 100, 025, 000, 255, 000, 192)] // Red-magenta [DataRow(000, 100, 025, 000, 255, 000, 192)] // Red-magenta
[DataRow(000, 100, 050, 000, 255, 000, 128)] // Blue-red [DataRow(000, 100, 050, 000, 255, 000, 128)] // Blue-red
[DataRow(000, 100, 075, 000, 255, 000, 064)] // Light blue-red [DataRow(000, 100, 075, 000, 255, 000, 064)] // Light blue-red
public void ColorRGBtoCMYK(int cyan, int magenta, int yellow, int blackKey, int red, int green, int blue) public void ColorRGBtoCMYKTest(int cyan, int magenta, int yellow, int blackKey, int red, int green, int blue)
{ {
var color = Color.FromArgb(255, red, green, blue); var color = Color.FromArgb(255, red, green, blue);
var result = ColorHelper.ConvertToCMYKColor(color); var result = ColorHelper.ConvertToCMYKColor(color);
@ -156,8 +157,130 @@ namespace UnitTest_ColorPickerUI.Helpers
Assert.AreEqual(result.blackKey * 100d, blackKey, 0.5d); Assert.AreEqual(result.blackKey * 100d, blackKey, 0.5d);
} }
// values taken from https://en.wikipedia.org/wiki/HSL_and_HSV#Examples
[TestMethod] [TestMethod]
public void ColorRGBtoCMYKZeroDiv() [DataRow("FFFFFF", 000.0, 000.0, 100.0)] // white
[DataRow("808080", 000.0, 000.0, 050.0)] // gray
[DataRow("000000", 000.0, 000.0, 000.0)] // black
[DataRow("FF0000", 000.0, 100.0, 033.3)] // red
[DataRow("BFBF00", 060.0, 100.0, 050.0)] // yellow
[DataRow("008000", 120.0, 100.0, 016.7)] // green
[DataRow("80FFFF", 180.0, 040.0, 083.3)] // cyan
[DataRow("8080FF", 240.0, 025.0, 066.7)] // blue
[DataRow("BF40BF", 300.0, 057.1, 058.3)] // magenta
[DataRow("A0A424", 061.8, 069.9, 047.1)]
[DataRow("411BEA", 251.1, 075.6, 042.6)]
[DataRow("1EAC41", 134.9, 066.7, 034.9)]
[DataRow("F0C80E", 049.5, 091.1, 059.3)]
[DataRow("B430E5", 283.7, 068.6, 059.6)]
[DataRow("ED7651", 014.3, 044.6, 057.0)]
[DataRow("FEF888", 056.9, 036.3, 083.5)]
[DataRow("19CB97", 162.4, 080.0, 049.5)]
[DataRow("362698", 248.3, 053.3, 031.9)]
[DataRow("7E7EB8", 240.5, 013.5, 057.0)]
public void ColorRGBtoHSITest(string hexValue, double hue, double saturation, double intensity)
{
var red = int.Parse(hexValue.Substring(0, 2), NumberStyles.HexNumber);
var green = int.Parse(hexValue.Substring(2, 2), NumberStyles.HexNumber);
var blue = int.Parse(hexValue.Substring(4, 2), NumberStyles.HexNumber);
var color = Color.FromArgb(255, red, green, blue);
var result = ColorHelper.ConvertToHSIColor(color);
// hue[0°..360°]
Assert.AreEqual(result.hue, hue, 0.5d);
// saturation[0..1]
Assert.AreEqual(result.saturation * 100d, saturation, 0.5d);
// intensity[0..1]
Assert.AreEqual(result.intensity * 100d, intensity, 0.5d);
}
// values taken from https://en.wikipedia.org/wiki/HSL_and_HSV#Examples
// and manual convert via https://colorconv.com/hwb
[TestMethod]
[DataRow("FFFFFF", 000, 100, 000)] // white
[DataRow("808080", 000, 050, 050)] // gray
[DataRow("000000", 000, 000, 100)] // black
[DataRow("FF0000", 000, 000, 000)] // red
[DataRow("BFBF00", 060, 000, 025)] // yellow
[DataRow("008000", 120, 000, 050)] // green
[DataRow("80FFFF", 180, 050, 000)] // cyan
[DataRow("8080FF", 240, 050, 000)] // blue
[DataRow("BF40BF", 300, 025, 025)] // magenta
[DataRow("A0A424", 062, 014, 036)]
[DataRow("411BEA", 251, 011, 008)]
[DataRow("1EAC41", 135, 012, 033)]
[DataRow("F0C80E", 049, 005, 006)]
[DataRow("B430E5", 284, 019, 010)]
[DataRow("ED7651", 014, 032, 007)]
[DataRow("FEF888", 057, 053, 000)]
[DataRow("19CB97", 162, 010, 020)]
[DataRow("362698", 248, 015, 040)]
[DataRow("7E7EB8", 240, 049, 028)]
public void ColorRGBtoHWBTest(string hexValue, double hue, double whiteness, double blackness)
{
var red = int.Parse(hexValue.Substring(0, 2), NumberStyles.HexNumber);
var green = int.Parse(hexValue.Substring(2, 2), NumberStyles.HexNumber);
var blue = int.Parse(hexValue.Substring(4, 2), NumberStyles.HexNumber);
var color = Color.FromArgb(255, red, green, blue);
var result = ColorHelper.ConvertToHWBColor(color);
// hue[0°..360°]
Assert.AreEqual(result.hue, hue, 0.5d);
// whiteness[0..1]
Assert.AreEqual(result.whiteness * 100d, whiteness, 0.5d);
// blackness[0..1]
Assert.AreEqual(result.blackness * 100d, blackness, 0.5d);
}
// values taken from https://en.wikipedia.org/wiki/HSL_and_HSV#Examples
// and manual convert via https://colorconv.com/hwb
[TestMethod]
[DataRow("FFFFFF", "R0", 100, 000)] // white
[DataRow("808080", "R0", 050, 050)] // gray
[DataRow("000000", "R0", 000, 100)] // black
[DataRow("FF0000", "R0", 000, 000)] // red
[DataRow("BFBF00", "Y0", 000, 025)] // yellow
[DataRow("008000", "G0", 000, 050)] // green
[DataRow("80FFFF", "C0", 050, 000)] // cyan
[DataRow("8080FF", "B0", 050, 000)] // blue
[DataRow("BF40BF", "M0", 025, 025)] // magenta
[DataRow("A0A424", "Y3", 014, 036)]
[DataRow("411BEA", "B18", 011, 008)]
[DataRow("1EAC41", "G25", 012, 033)]
[DataRow("F0C80E", "R82", 005, 006)]
[DataRow("B430E5", "B73", 019, 010)]
[DataRow("ED7651", "R24", 032, 007)]
[DataRow("FEF888", "R95", 053, 000)]
[DataRow("19CB97", "G71", 010, 020)]
[DataRow("362698", "B14", 015, 040)]
[DataRow("7E7EB8", "B0", 049, 028)]
public void ColorRGBtoNColTest(string hexValue, string hue, double whiteness, double blackness)
{
var red = int.Parse(hexValue.Substring(0, 2), NumberStyles.HexNumber);
var green = int.Parse(hexValue.Substring(2, 2), NumberStyles.HexNumber);
var blue = int.Parse(hexValue.Substring(4, 2), NumberStyles.HexNumber);
var color = Color.FromArgb(255, red, green, blue);
var result = ColorHelper.ConvertToNaturalColor(color);
// hue
Assert.AreEqual(result.hue, hue);
// whiteness[0..1]
Assert.AreEqual(result.whiteness * 100d, whiteness, 0.5d);
// blackness[0..1]
Assert.AreEqual(result.blackness * 100d, blackness, 0.5d);
}
[TestMethod]
public void ColorRGBtoCMYKZeroDivTest()
{ {
for(var red = 0; red < 256; red++) for(var red = 0; red < 256; red++)
{ {

View File

@ -1,5 +1,5 @@
using ColorPicker.Helpers; using ColorPicker.Helpers;
using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Drawing; using System.Drawing;
@ -11,11 +11,15 @@ namespace UnitTest_ColorPickerUI.Helpers
[TestMethod] [TestMethod]
[DataRow(ColorRepresentationType.CMYK, "cmyk(0%, 0%, 0%, 100%)")] [DataRow(ColorRepresentationType.CMYK, "cmyk(0%, 0%, 0%, 100%)")]
[DataRow(ColorRepresentationType.HEX, "#000000")] [DataRow(ColorRepresentationType.HEX, "#000000")]
[DataRow(ColorRepresentationType.NCol, "R0, 0%, 100%")]
[DataRow(ColorRepresentationType.HSB, "hsb(0, 0%, 0%)")]
[DataRow(ColorRepresentationType.HSI, "hsi(0, 0%, 0%)")]
[DataRow(ColorRepresentationType.HSL, "hsl(0, 0%, 0%)")] [DataRow(ColorRepresentationType.HSL, "hsl(0, 0%, 0%)")]
[DataRow(ColorRepresentationType.HSV, "hsv(0, 0%, 0%)")] [DataRow(ColorRepresentationType.HSV, "hsv(0, 0%, 0%)")]
[DataRow(ColorRepresentationType.HWB, "hwb(0, 0%, 100%)")]
[DataRow(ColorRepresentationType.RGB, "rgb(0, 0, 0)")] [DataRow(ColorRepresentationType.RGB, "rgb(0, 0, 0)")]
public void ColorRGBtoCMYKZeroDiv(ColorRepresentationType type, string expected) public void GetStringRepresentationTest(ColorRepresentationType type, string expected)
{ {
var result = ColorRepresentationHelper.GetStringRepresentation(Color.Black, type); var result = ColorRepresentationHelper.GetStringRepresentation(Color.Black, type);
Assert.AreEqual(result, expected); Assert.AreEqual(result, expected);