Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
LexaGV committed Nov 4, 2022
1 parent 7e5671f commit a900ea2
Show file tree
Hide file tree
Showing 14 changed files with 1,087 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.vs/
.git/
bin_Debug/
bin_Release/
*.user
107 changes: 107 additions & 0 deletions ClrHlp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Media;

namespace WPFColorLib
{
public class ClrHlp
{
public static (int, int, int) ColorToHSL(Color rgb)
{
var r = (rgb.R / 255.0);
var g = (rgb.G / 255.0);
var b = (rgb.B / 255.0);

var min = Math.Min(Math.Min(r, g), b);
var max = Math.Max(Math.Max(r, g), b);
var delta = max - min;

var lum = (max + min) / 2;
double hue, sat;

if (delta == 0)
{
hue = 0.0;
sat = 0.0;
}
else
{
sat = (lum <= 0.5) ? (delta / (max + min)) : (delta / (2 - max - min));

if (r == max)
{
hue = ((g - b) / 6.0) / delta;
}
else if (g == max)
{
hue = (1.0 / 3.0) + ((b - r) / 6.0) / delta;
}
else
{
hue = (2.0 / 3.0) + ((r - g) / 6.0) / delta;
}

if (hue < 0)
hue += 1;
if (hue > 1)
hue -= 1;
}

return ((int)(hue*360), (int)(sat*100), (int)(lum*100));
}

public static Regex reHexRGB = new Regex(@"([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})", RegexOptions.Compiled | RegexOptions.IgnoreCase);

public static Color Hex2color(string s)
{
var m = reHexRGB.Match(s);
if (m.Success)
return Color.FromRgb(Hex2Byte(m.Groups[1].Value), Hex2Byte(m.Groups[2].Value), Hex2Byte(m.Groups[3].Value));
throw new ArgumentException($"Invalid hex RGB value '{s}'");
}

public static string Color2hex(Color clr)
{
return clr.R.ToString("X02") + clr.G.ToString("X02") + clr.B.ToString("X02");
}

public static byte Hex2Byte(string hex)
{
return byte.Parse(hex, NumberStyles.AllowHexSpecifier);
}

public static int HSL2RGBInt(int h, int s, int l)
{
var rgb = HSL2RGB(h, s, l);
return rgb[0] << 16 | rgb[1] << 8 | rgb[2];
}

public static int[] HSL2RGB(int h, int s, int l)
{
var rgb = new int[3];

var baseColor = (h + 60) % 360 / 120;
var shift = (h + 60) % 360 - (120 * baseColor + 60);
var secondaryColor = (baseColor + (shift >= 0 ? 1 : -1) + 3) % 3;

//Setting Hue
rgb[baseColor] = 255;
rgb[secondaryColor] = (int)((Math.Abs(shift) / 60.0f) * 255.0f);

//Setting Saturation
for (var i = 0; i < 3; i++)
rgb[i] += (int)((255 - rgb[i]) * ((100 - s) / 100.0f));

//Setting Value
for (var i = 0; i < 3; i++)
rgb[i] -= (int)(rgb[i] * (100 - l) / 100.0f);

return rgb;
}
}
}
19 changes: 19 additions & 0 deletions HSLColorSelector.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<UserControl x:Class="WPFColorLib.HSLColorSelector" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeChanged="Control_Resized">
<DockPanel>
<StackPanel DockPanel.Dock="Top">
<Slider Name="slidHue" Maximum="359" TickFrequency="10" TickPlacement="BottomRight" />
<Border Name="brdHue" BorderThickness="1" BorderBrush="DarkGray" Margin="1">
<Image Name="imgHue" Height="20" />
</Border>
</StackPanel>
<Border Name="brdSaturLight" BorderThickness="1" BorderBrush="DarkGray" Margin="1">
<Grid>
<Image Name="imgSaturLight" MinHeight="100" MouseDown="imgSaturLight_MouDown" MouseMove="imgSaturLight_MouMove" MouseUp="imgSaturLight_MouUp" />
<Canvas Name="cnvSaturLight">
<Ellipse Name="ellClrTarget" Width="7" Height="7" StrokeThickness="2" Stroke="Aqua" />
</Canvas>
</Grid>
</Border>
</DockPanel>
</UserControl>
153 changes: 153 additions & 0 deletions HSLColorSelector.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace WPFColorLib
{
public partial class HSLColorSelector : UserControl
{
public HSLColorSelector()
{
InitializeComponent();

slidHue.ValueChanged += SlidHue_ValueChanged;
}

public event EventHandler<Tuple<byte, byte, byte>> ColorRGBChanged;
public event EventHandler<Tuple<int, int, int>> ColorHSLChanged;// 0..360, 0..100, 0..100

void Control_Resized(object sender, SizeChangedEventArgs e)
{
#region recreate hue bar
var imgW = (int)(brdHue.ActualWidth - 2);
var imgH = (int)(brdHue.ActualHeight - 2);
imgHue.Source = new WriteableBitmap(imgW, imgH, 96, 96, PixelFormats.Bgr32, null);
DrawHueBar();
#endregion

#region recreate Saturation/Lightness area
imgW = (int)(brdSaturLight.ActualWidth - 2);
imgH = (int)(brdSaturLight.ActualHeight - 2);
imgSaturLight.Source = new WriteableBitmap(imgW, imgH, 96, 96, PixelFormats.Bgr32, null);
RedrawSaturLight(slidHue.Value);
#endregion
}

void DrawHueBar()
{
var recW = (int)(brdHue.ActualWidth - 2);
var recH = (int)(brdHue.ActualHeight - 2);
var k = 360.0 / recW;
var bmp = (WriteableBitmap)imgHue.Source;
bmp.Lock();
unsafe {
var buf = bmp.BackBuffer;
// fill the first line of hue bar
for(int x=0; x < recW; x++){
int hue = (int)Math.Floor(x * k);
*((int*)(buf + x * 4)) = ClrHlp.HSL2RGBInt(hue, 100, 100);
}
var origColorLine = new Span<byte>(buf.ToPointer(), bmp.BackBufferStride);
for (int y = 1; y < recH; y++) {
var destColorLine = new Span<byte>((buf + y * bmp.BackBufferStride).ToPointer(), bmp.BackBufferStride);
origColorLine.CopyTo(destColorLine);
}
}
bmp.AddDirtyRect(new Int32Rect(0, 0, recW, recH));
bmp.Unlock();
}

void SlidHue_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
RedrawSaturLight(e.NewValue);

// calc contrast color for ellipse(target above color)
var contrastHue = ((int)e.NewValue + 180) % 360;// opposite side of color ring
var contrastColor = ClrHlp.HSL2RGB(contrastHue, 100, 100);
ellClrTarget.Stroke = new SolidColorBrush(Color.FromRgb((byte)contrastColor[0], (byte)contrastColor[1], (byte)contrastColor[2]));
}

void RedrawSaturLight(double newHue)
{
var hue = (int)newHue;
var recW = (int)(brdSaturLight.ActualWidth - 2);
var recH = (int)(brdSaturLight.ActualHeight - 2);
var kX = 100.0 / recW;
var kY = 100.0 / recH;
var bmp = (WriteableBitmap)imgSaturLight.Source;
bmp.Lock();
unsafe {
var buf = bmp.BackBuffer;
for (int y = 0; y < recH; y++) {
var lineStart = buf + y * bmp.BackBufferStride;
for (int x = 0; x < recW; x++) {
*((int*)(lineStart + x * 4)) = ClrHlp.HSL2RGBInt(hue, (int)(kX * x), (int)(kY * y));
}
}
}
bmp.AddDirtyRect(new Int32Rect(0, 0, recW, recH));
bmp.Unlock();
}

bool trackColorMode = false;

void imgSaturLight_MouDown(object sender, MouseButtonEventArgs e)
{
trackColorMode = true;
imgSaturLight.CaptureMouse();

SelectNewColor(e.GetPosition(imgSaturLight));
}

void imgSaturLight_MouMove(object sender, MouseEventArgs e)
{
if (!trackColorMode) return;

SelectNewColor(e.GetPosition(imgSaturLight));
}

void SelectNewColor(Point mou)
{
var recW = (int)(brdSaturLight.ActualWidth - 2);
var recH = (int)(brdSaturLight.ActualHeight - 2);
var kX = 100.0 / recW;
var kY = 100.0 / recH;

if (mou.X < 0)
mou.X = 0;
else if (mou.X >= recW)
mou.X = recW - 1;
if (mou.Y < 0)
mou.Y = 0;
else if (mou.Y >= recH)
mou.Y = recH - 1;

Canvas.SetLeft(ellClrTarget, mou.X-2);
Canvas.SetTop(ellClrTarget, mou.Y-2);

var hue = (int)slidHue.Value;
var sat = (int)(mou.X * kX);
var lum = (int)(mou.Y * kY);
ColorHSLChanged?.Invoke(this, new Tuple<int, int, int>(hue, sat, lum));

if (ColorRGBChanged != null) {
var clr = ClrHlp.HSL2RGB(hue, sat, lum);
ColorRGBChanged.Invoke(this, new Tuple<byte, byte, byte>((byte)clr[0], (byte)clr[1], (byte)clr[2]));
}
}

void imgSaturLight_MouUp(object sender, MouseButtonEventArgs e)
{
trackColorMode = false;
imgSaturLight.ReleaseMouseCapture();
}
}
}
55 changes: 55 additions & 0 deletions Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("WPFColorLib")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WPFColorLib")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.

//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]


[assembly:ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]


// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
Loading

0 comments on commit a900ea2

Please sign in to comment.