Skip to content

Commit

Permalink
feat: added support for Tinycolor type (#16)
Browse files Browse the repository at this point in the history
* add tinyColor type

* add tinyColor tests
  • Loading branch information
yoli799480165 authored Dec 22, 2023
1 parent 46e047e commit 1bd9366
Show file tree
Hide file tree
Showing 8 changed files with 1,916 additions and 1 deletion.
137 changes: 137 additions & 0 deletions src/Colors/ColorInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
using System;

namespace CssInCSharp.Colors
{
public record struct RGB(StringNumber R, StringNumber G, StringNumber B);
public record struct RGBA(StringNumber R, StringNumber G, StringNumber B, StringNumber? A);
public record struct HSL(StringNumber H, StringNumber S, StringNumber L);
public record struct HSLA(StringNumber H, StringNumber S, StringNumber L, StringNumber? A);
public record struct HSV(StringNumber H, StringNumber S, StringNumber V);
public record struct HSVA(StringNumber H, StringNumber S, StringNumber V, StringNumber? A);
public record struct RatioInput(StringNumber R, StringNumber G, StringNumber B, StringNumber? A = default);

public sealed class ColorInput
{
private readonly int _index;
private readonly string _value0;
private readonly int _value1;
private readonly TinyColor _value2;

public ColorInput()
{
_index = -1;
}

private ColorInput(
int index,
string value0 = default,
int value1 = default,
TinyColor value2 = default,
RGB rgb = default,
RGBA rgba = default,
HSL hsl = default,
HSLA hsla = default,
HSV hsv = default,
HSVA hsva = default)
{
_index = index;
_value0 = value0;
_value1 = value1;
_value2 = value2;

if (rgb != default)
{
R = rgb.R;
G = rgb.G;
B = rgb.B;
}
if (rgba != default)
{
R = rgba.R;
G = rgba.G;
B = rgba.B;
A = rgba.A;
}
if (hsl != default)
{
H = hsl.H;
S = hsl.S;
L = hsl.L;
}
if (hsla != default)
{
H = hsla.H;
S = hsla.S;
L = hsla.L;
A = hsla.A;
}
if (hsv != default)
{
H = hsv.H;
S = hsv.S;
V = hsv.V;
}
if (hsva != default)
{
H = hsva.H;
S = hsva.S;
V = hsva.V;
A = hsva.A;
}
}

public static implicit operator ColorInput(string value) => new(0, value0: value);
public static implicit operator ColorInput(int value) => new(1, value1: value);
public static implicit operator ColorInput(TinyColor value) => new(2, value2: value);
public static implicit operator ColorInput(RGB value) => new(-1, rgb: value);
public static implicit operator ColorInput(RGBA value) => new(-1, rgba: value);
public static implicit operator ColorInput(HSL value) => new(-1, hsl: value);
public static implicit operator ColorInput(HSLA value) => new(-1, hsla: value);
public static implicit operator ColorInput(HSV value) => new(-1, hsv: value);
public static implicit operator ColorInput(HSVA value) => new(-1, hsva: value);

public StringNumber R { get; set; }
public StringNumber G { get; set; }
public StringNumber B { get; set; }
public StringNumber? A { get; set; }
public StringNumber H { get; set; }
public StringNumber S { get; set; }
public StringNumber V { get; set; }
public StringNumber L { get; set; }

public string Format { get; set; }

public bool IsObject => _index == -1;

public bool IsString => _index == 0;

public bool IsNumber => _index == 1;

public bool IsColor => _index == 2;

public string AsString =>
_index == 0 ?
_value0 :
throw new InvalidOperationException();

public int AsNumber =>
_index == 1 ?
_value1 :
throw new InvalidOperationException();

public TinyColor AsColor =>
_index == 2 ?
_value2 :
throw new InvalidOperationException();

public override string ToString()
{
return _index switch
{
0 => _value0,
1 => _value1.ToString(),
_ => throw new InvalidOperationException()
};
}
}
}
225 changes: 225 additions & 0 deletions src/Colors/Conversion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
using System;
using static CssInCSharp.Colors.Util;

namespace CssInCSharp.Colors
{
internal static class Conversion
{
public static RGB NumberInputToObject(int color)
{
return new RGB(color >> 16, (color & 0xFF00) >> 8, color & 0xFF);
}

public static string RgbToHex(double r, double g, double b, bool allow3Char)
{
var hex = new []
{
((int)MathRound(r)).ToString("x2"),
((int)MathRound(g)).ToString("x2"),
((int)MathRound(b)).ToString("x2"),
};

if (allow3Char &&
hex[0].StartsWith(hex[0].CharAt(1)) &&
hex[1].StartsWith(hex[1].CharAt(1)) &&
hex[2].StartsWith(hex[2].CharAt(1))
)
{
return hex[0].CharAt(0) + hex[1].CharAt(0) + hex[2].CharAt(0);
}

return hex.Join("");
}

public static string RgbaToHex(double r, double g, double b, double a, bool allow4Char)
{
var hex = new[]
{
((int)MathRound(r)).ToString("x2"),
((int)MathRound(g)).ToString("x2"),
((int)MathRound(b)).ToString("x2"),
((int)MathRound(a * 255)).ToString("x2"),
};

if (
allow4Char &&
hex[0].StartsWith(hex[0].CharAt(1)) &&
hex[1].StartsWith(hex[1].CharAt(1)) &&
hex[2].StartsWith(hex[2].CharAt(1)) &&
hex[3].StartsWith(hex[3].CharAt(1))
)
{
return hex[0].CharAt(0) + hex[1].CharAt(0) + hex[2].CharAt(0) + hex[3].CharAt(0);
}

return hex.Join("");
}

public static RGB RgbToRgb(StringNumber r, StringNumber g, StringNumber b)
{
return new RGB
{
R = Bound01(r, 255) * 255,
G = Bound01(g, 255) * 255,
B = Bound01(b, 255) * 255,
};
}

public static HSV RgbToHsv(double r, double g, double b)
{
r = Bound01(r, 255).AsNumber;
g = Bound01(g, 255).AsNumber;
b = Bound01(b, 255).AsNumber;

var max = MathMax(r, g, b);
var min = MathMin(r, g, b);
double h = 0;
var v = max;
var d = max - min;
var s = max == 0 ? 0 : d / max;

if (max == min)
{
h = 0; // achromatic
}
else
{
switch (max)
{
case var _ when max == r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case var _ when max == g:
h = (b - r) / d + 2;
break;
case var _ when max == b:
h = (r - g) / d + 4;
break;
default:
break;
}

h /= 6;
}

return new HSV(h, s, v);
}

public static HSL RgbToHsl(double r, double g, double b)
{
r = Bound01(r, 255).AsNumber;
g = Bound01(g, 255).AsNumber;
b = Bound01(b, 255).AsNumber;

var max = MathMax(r, g, b);
var min = MathMin(r, g, b);
double h = 0;
double s = 0;
var l = (max + min) / 2;

if (max == min)
{
s = 0;
h = 0; // achromatic
}
else
{
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max)
{
case var _ when max == r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case var _ when max == g:
h = (b - r) / d + 2;
break;
case var _ when max == b:
h = (r - g) / d + 4;
break;
default:
break;
}

h /= 6;
}

return new HSL(h, s, l);
}

public static RGB HsvToRgb(StringNumber h, StringNumber s, StringNumber v)
{
h = Bound01(h, 360) * 6;
s = Bound01(s, 100);
v = Bound01(v, 100);
var i = Math.Floor(h.AsNumber);
var f = h - i;
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
var mod = (int)i % 6;
var r = new double[] { v.AsNumber, q, p, p, t, v.AsNumber }[mod];
var g = new double[] { t, v.AsNumber, v.AsNumber, q, p, p }[mod];
var b = new double[] { p, p, t, v.AsNumber, v.AsNumber, q }[mod];
return new RGB(r * 255, g * 255, b * 255);
}

public static RGB HslToRgb(StringNumber h, StringNumber s, StringNumber l)
{
double r;
double g;
double b;

h = Bound01(h, 360);
s = Bound01(s, 100);
l = Bound01(l, 100);

if (s == 0)
{
// achromatic
g = l.AsNumber;
b = l.AsNumber;
r = l.AsNumber;
}
else
{
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = Hue2Rgb(p, q, h + 1d / 3);
g = Hue2Rgb(p, q, h.AsNumber);
b = Hue2Rgb(p, q, h - 1d / 3);
}

return new RGB(r * 255, g * 255, b * 255);
}

public static double Hue2Rgb(double p, double q, double t)
{
if (t < 0)
{
t += 1;
}
if (t > 1)
{
t -= 1;
}

if (t < 1d / 6)
{
return p + (q - p) * (6 * t);
}

if (t < 1d / 2)
{
return q;
}

if (t < 2d / 3)
{
return p + (q - p) * (2d / 3 - t) * 6;
}

return p;
}
}
}
Loading

0 comments on commit 1bd9366

Please sign in to comment.