Skip to content

Commit a900ea2

Browse files
committed
Initial commit
1 parent 7e5671f commit a900ea2

14 files changed

+1087
-2
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.vs/
2+
.git/
3+
bin_Debug/
4+
bin_Release/
5+
*.user

ClrHlp.cs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Text.RegularExpressions;
7+
using System.Threading.Tasks;
8+
using System.Windows.Media;
9+
10+
namespace WPFColorLib
11+
{
12+
public class ClrHlp
13+
{
14+
public static (int, int, int) ColorToHSL(Color rgb)
15+
{
16+
var r = (rgb.R / 255.0);
17+
var g = (rgb.G / 255.0);
18+
var b = (rgb.B / 255.0);
19+
20+
var min = Math.Min(Math.Min(r, g), b);
21+
var max = Math.Max(Math.Max(r, g), b);
22+
var delta = max - min;
23+
24+
var lum = (max + min) / 2;
25+
double hue, sat;
26+
27+
if (delta == 0)
28+
{
29+
hue = 0.0;
30+
sat = 0.0;
31+
}
32+
else
33+
{
34+
sat = (lum <= 0.5) ? (delta / (max + min)) : (delta / (2 - max - min));
35+
36+
if (r == max)
37+
{
38+
hue = ((g - b) / 6.0) / delta;
39+
}
40+
else if (g == max)
41+
{
42+
hue = (1.0 / 3.0) + ((b - r) / 6.0) / delta;
43+
}
44+
else
45+
{
46+
hue = (2.0 / 3.0) + ((r - g) / 6.0) / delta;
47+
}
48+
49+
if (hue < 0)
50+
hue += 1;
51+
if (hue > 1)
52+
hue -= 1;
53+
}
54+
55+
return ((int)(hue*360), (int)(sat*100), (int)(lum*100));
56+
}
57+
58+
public static Regex reHexRGB = new Regex(@"([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})", RegexOptions.Compiled | RegexOptions.IgnoreCase);
59+
60+
public static Color Hex2color(string s)
61+
{
62+
var m = reHexRGB.Match(s);
63+
if (m.Success)
64+
return Color.FromRgb(Hex2Byte(m.Groups[1].Value), Hex2Byte(m.Groups[2].Value), Hex2Byte(m.Groups[3].Value));
65+
throw new ArgumentException($"Invalid hex RGB value '{s}'");
66+
}
67+
68+
public static string Color2hex(Color clr)
69+
{
70+
return clr.R.ToString("X02") + clr.G.ToString("X02") + clr.B.ToString("X02");
71+
}
72+
73+
public static byte Hex2Byte(string hex)
74+
{
75+
return byte.Parse(hex, NumberStyles.AllowHexSpecifier);
76+
}
77+
78+
public static int HSL2RGBInt(int h, int s, int l)
79+
{
80+
var rgb = HSL2RGB(h, s, l);
81+
return rgb[0] << 16 | rgb[1] << 8 | rgb[2];
82+
}
83+
84+
public static int[] HSL2RGB(int h, int s, int l)
85+
{
86+
var rgb = new int[3];
87+
88+
var baseColor = (h + 60) % 360 / 120;
89+
var shift = (h + 60) % 360 - (120 * baseColor + 60);
90+
var secondaryColor = (baseColor + (shift >= 0 ? 1 : -1) + 3) % 3;
91+
92+
//Setting Hue
93+
rgb[baseColor] = 255;
94+
rgb[secondaryColor] = (int)((Math.Abs(shift) / 60.0f) * 255.0f);
95+
96+
//Setting Saturation
97+
for (var i = 0; i < 3; i++)
98+
rgb[i] += (int)((255 - rgb[i]) * ((100 - s) / 100.0f));
99+
100+
//Setting Value
101+
for (var i = 0; i < 3; i++)
102+
rgb[i] -= (int)(rgb[i] * (100 - l) / 100.0f);
103+
104+
return rgb;
105+
}
106+
}
107+
}

HSLColorSelector.xaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<UserControl x:Class="WPFColorLib.HSLColorSelector" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
2+
SizeChanged="Control_Resized">
3+
<DockPanel>
4+
<StackPanel DockPanel.Dock="Top">
5+
<Slider Name="slidHue" Maximum="359" TickFrequency="10" TickPlacement="BottomRight" />
6+
<Border Name="brdHue" BorderThickness="1" BorderBrush="DarkGray" Margin="1">
7+
<Image Name="imgHue" Height="20" />
8+
</Border>
9+
</StackPanel>
10+
<Border Name="brdSaturLight" BorderThickness="1" BorderBrush="DarkGray" Margin="1">
11+
<Grid>
12+
<Image Name="imgSaturLight" MinHeight="100" MouseDown="imgSaturLight_MouDown" MouseMove="imgSaturLight_MouMove" MouseUp="imgSaturLight_MouUp" />
13+
<Canvas Name="cnvSaturLight">
14+
<Ellipse Name="ellClrTarget" Width="7" Height="7" StrokeThickness="2" Stroke="Aqua" />
15+
</Canvas>
16+
</Grid>
17+
</Border>
18+
</DockPanel>
19+
</UserControl>

HSLColorSelector.xaml.cs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using System.Windows;
7+
using System.Windows.Controls;
8+
using System.Windows.Input;
9+
using System.Windows.Media;
10+
using System.Windows.Media.Imaging;
11+
using System.Windows.Shapes;
12+
13+
namespace WPFColorLib
14+
{
15+
public partial class HSLColorSelector : UserControl
16+
{
17+
public HSLColorSelector()
18+
{
19+
InitializeComponent();
20+
21+
slidHue.ValueChanged += SlidHue_ValueChanged;
22+
}
23+
24+
public event EventHandler<Tuple<byte, byte, byte>> ColorRGBChanged;
25+
public event EventHandler<Tuple<int, int, int>> ColorHSLChanged;// 0..360, 0..100, 0..100
26+
27+
void Control_Resized(object sender, SizeChangedEventArgs e)
28+
{
29+
#region recreate hue bar
30+
var imgW = (int)(brdHue.ActualWidth - 2);
31+
var imgH = (int)(brdHue.ActualHeight - 2);
32+
imgHue.Source = new WriteableBitmap(imgW, imgH, 96, 96, PixelFormats.Bgr32, null);
33+
DrawHueBar();
34+
#endregion
35+
36+
#region recreate Saturation/Lightness area
37+
imgW = (int)(brdSaturLight.ActualWidth - 2);
38+
imgH = (int)(brdSaturLight.ActualHeight - 2);
39+
imgSaturLight.Source = new WriteableBitmap(imgW, imgH, 96, 96, PixelFormats.Bgr32, null);
40+
RedrawSaturLight(slidHue.Value);
41+
#endregion
42+
}
43+
44+
void DrawHueBar()
45+
{
46+
var recW = (int)(brdHue.ActualWidth - 2);
47+
var recH = (int)(brdHue.ActualHeight - 2);
48+
var k = 360.0 / recW;
49+
var bmp = (WriteableBitmap)imgHue.Source;
50+
bmp.Lock();
51+
unsafe {
52+
var buf = bmp.BackBuffer;
53+
// fill the first line of hue bar
54+
for(int x=0; x < recW; x++){
55+
int hue = (int)Math.Floor(x * k);
56+
*((int*)(buf + x * 4)) = ClrHlp.HSL2RGBInt(hue, 100, 100);
57+
}
58+
var origColorLine = new Span<byte>(buf.ToPointer(), bmp.BackBufferStride);
59+
for (int y = 1; y < recH; y++) {
60+
var destColorLine = new Span<byte>((buf + y * bmp.BackBufferStride).ToPointer(), bmp.BackBufferStride);
61+
origColorLine.CopyTo(destColorLine);
62+
}
63+
}
64+
bmp.AddDirtyRect(new Int32Rect(0, 0, recW, recH));
65+
bmp.Unlock();
66+
}
67+
68+
void SlidHue_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
69+
{
70+
RedrawSaturLight(e.NewValue);
71+
72+
// calc contrast color for ellipse(target above color)
73+
var contrastHue = ((int)e.NewValue + 180) % 360;// opposite side of color ring
74+
var contrastColor = ClrHlp.HSL2RGB(contrastHue, 100, 100);
75+
ellClrTarget.Stroke = new SolidColorBrush(Color.FromRgb((byte)contrastColor[0], (byte)contrastColor[1], (byte)contrastColor[2]));
76+
}
77+
78+
void RedrawSaturLight(double newHue)
79+
{
80+
var hue = (int)newHue;
81+
var recW = (int)(brdSaturLight.ActualWidth - 2);
82+
var recH = (int)(brdSaturLight.ActualHeight - 2);
83+
var kX = 100.0 / recW;
84+
var kY = 100.0 / recH;
85+
var bmp = (WriteableBitmap)imgSaturLight.Source;
86+
bmp.Lock();
87+
unsafe {
88+
var buf = bmp.BackBuffer;
89+
for (int y = 0; y < recH; y++) {
90+
var lineStart = buf + y * bmp.BackBufferStride;
91+
for (int x = 0; x < recW; x++) {
92+
*((int*)(lineStart + x * 4)) = ClrHlp.HSL2RGBInt(hue, (int)(kX * x), (int)(kY * y));
93+
}
94+
}
95+
}
96+
bmp.AddDirtyRect(new Int32Rect(0, 0, recW, recH));
97+
bmp.Unlock();
98+
}
99+
100+
bool trackColorMode = false;
101+
102+
void imgSaturLight_MouDown(object sender, MouseButtonEventArgs e)
103+
{
104+
trackColorMode = true;
105+
imgSaturLight.CaptureMouse();
106+
107+
SelectNewColor(e.GetPosition(imgSaturLight));
108+
}
109+
110+
void imgSaturLight_MouMove(object sender, MouseEventArgs e)
111+
{
112+
if (!trackColorMode) return;
113+
114+
SelectNewColor(e.GetPosition(imgSaturLight));
115+
}
116+
117+
void SelectNewColor(Point mou)
118+
{
119+
var recW = (int)(brdSaturLight.ActualWidth - 2);
120+
var recH = (int)(brdSaturLight.ActualHeight - 2);
121+
var kX = 100.0 / recW;
122+
var kY = 100.0 / recH;
123+
124+
if (mou.X < 0)
125+
mou.X = 0;
126+
else if (mou.X >= recW)
127+
mou.X = recW - 1;
128+
if (mou.Y < 0)
129+
mou.Y = 0;
130+
else if (mou.Y >= recH)
131+
mou.Y = recH - 1;
132+
133+
Canvas.SetLeft(ellClrTarget, mou.X-2);
134+
Canvas.SetTop(ellClrTarget, mou.Y-2);
135+
136+
var hue = (int)slidHue.Value;
137+
var sat = (int)(mou.X * kX);
138+
var lum = (int)(mou.Y * kY);
139+
ColorHSLChanged?.Invoke(this, new Tuple<int, int, int>(hue, sat, lum));
140+
141+
if (ColorRGBChanged != null) {
142+
var clr = ClrHlp.HSL2RGB(hue, sat, lum);
143+
ColorRGBChanged.Invoke(this, new Tuple<byte, byte, byte>((byte)clr[0], (byte)clr[1], (byte)clr[2]));
144+
}
145+
}
146+
147+
void imgSaturLight_MouUp(object sender, MouseButtonEventArgs e)
148+
{
149+
trackColorMode = false;
150+
imgSaturLight.ReleaseMouseCapture();
151+
}
152+
}
153+
}

Properties/AssemblyInfo.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System.Reflection;
2+
using System.Resources;
3+
using System.Runtime.CompilerServices;
4+
using System.Runtime.InteropServices;
5+
using System.Windows;
6+
7+
// General Information about an assembly is controlled through the following
8+
// set of attributes. Change these attribute values to modify the information
9+
// associated with an assembly.
10+
[assembly: AssemblyTitle("WPFColorLib")]
11+
[assembly: AssemblyDescription("")]
12+
[assembly: AssemblyConfiguration("")]
13+
[assembly: AssemblyCompany("")]
14+
[assembly: AssemblyProduct("WPFColorLib")]
15+
[assembly: AssemblyCopyright("Copyright © 2022")]
16+
[assembly: AssemblyTrademark("")]
17+
[assembly: AssemblyCulture("")]
18+
19+
// Setting ComVisible to false makes the types in this assembly not visible
20+
// to COM components. If you need to access a type in this assembly from
21+
// COM, set the ComVisible attribute to true on that type.
22+
[assembly: ComVisible(false)]
23+
24+
//In order to begin building localizable applications, set
25+
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
26+
//inside a <PropertyGroup>. For example, if you are using US english
27+
//in your source files, set the <UICulture> to en-US. Then uncomment
28+
//the NeutralResourceLanguage attribute below. Update the "en-US" in
29+
//the line below to match the UICulture setting in the project file.
30+
31+
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32+
33+
34+
[assembly:ThemeInfo(
35+
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36+
//(used if a resource is not found in the page,
37+
// or application resource dictionaries)
38+
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39+
//(used if a resource is not found in the page,
40+
// app, or any theme specific resource dictionaries)
41+
)]
42+
43+
44+
// Version information for an assembly consists of the following four values:
45+
//
46+
// Major Version
47+
// Minor Version
48+
// Build Number
49+
// Revision
50+
//
51+
// You can specify all the values or you can default the Build and Revision Numbers
52+
// by using the '*' as shown below:
53+
// [assembly: AssemblyVersion("1.0.*")]
54+
[assembly: AssemblyVersion("1.0.0.0")]
55+
[assembly: AssemblyFileVersion("1.0.0.0")]

0 commit comments

Comments
 (0)