119 lines
3.3 KiB
C#
119 lines
3.3 KiB
C#
|
using System.Numerics;
|
||
|
|
||
|
namespace ExtraChat.Util;
|
||
|
|
||
|
internal static class ColourUtil {
|
||
|
internal static uint RgbaToAbgr(uint rgba) {
|
||
|
return (rgba >> 24) // red
|
||
|
| ((rgba & 0x0000ff00) << 8) // blue
|
||
|
| ((rgba & 0x00ff0000) >> 8) // green
|
||
|
| ((rgba & 0x000000ff) << 24); // alpha
|
||
|
}
|
||
|
|
||
|
internal static (double, double, double, double) ExplodeRgba(uint rgba) {
|
||
|
// separate RGBA values
|
||
|
var r = (byte) ((rgba >> 24) & 0xff);
|
||
|
var g = (byte) ((rgba >> 16) & 0xff);
|
||
|
var b = (byte) ((rgba >> 8) & 0xff);
|
||
|
var a = (byte) (rgba & 0xff);
|
||
|
|
||
|
// convert RGBA to floats
|
||
|
var rf = r / 255d;
|
||
|
var gf = g / 255d;
|
||
|
var bf = b / 255d;
|
||
|
var af = a / 255d;
|
||
|
|
||
|
return (rf, gf, bf, af);
|
||
|
}
|
||
|
|
||
|
internal static Vector4 RgbaToHsl(uint rgba) {
|
||
|
var (rf, gf, bf, af) = ExplodeRgba(rgba);
|
||
|
|
||
|
// determine hue
|
||
|
var max = Math.Max(rf, Math.Max(gf, bf));
|
||
|
var min = Math.Min(rf, Math.Min(gf, bf));
|
||
|
var chroma = max - min;
|
||
|
var hPrime = 0d;
|
||
|
if (chroma == 0) {
|
||
|
hPrime = 0d;
|
||
|
} else if (Math.Abs(rf - max) < 0.0001) {
|
||
|
hPrime = ((gf - bf) / chroma) % 6;
|
||
|
} else if (Math.Abs(gf - max) < 0.0001) {
|
||
|
hPrime = 2 + (bf - rf) / chroma;
|
||
|
} else if (Math.Abs(bf - max) < 0.0001) {
|
||
|
hPrime = 4 + (rf - gf) / chroma;
|
||
|
}
|
||
|
|
||
|
var h = hPrime * 60f;
|
||
|
|
||
|
// determine lightness
|
||
|
var l = (min + max) / 2f;
|
||
|
|
||
|
// determine saturation
|
||
|
double s;
|
||
|
if (l is 0 or 1) {
|
||
|
s = 0d;
|
||
|
} else {
|
||
|
s = chroma / (1 - Math.Abs(2 * l - 1));
|
||
|
}
|
||
|
|
||
|
return new Vector4((float) h, (float) s, (float) l, (float) af);
|
||
|
}
|
||
|
|
||
|
internal static Vector4 RgbaToHsv(uint rgba) {
|
||
|
var (rf, gf, bf, af) = ExplodeRgba(rgba);
|
||
|
|
||
|
// determine hue
|
||
|
var max = Math.Max(rf, Math.Max(gf, bf));
|
||
|
var min = Math.Min(rf, Math.Min(gf, bf));
|
||
|
var chroma = max - min;
|
||
|
var hPrime = 0d;
|
||
|
if (chroma == 0) {
|
||
|
hPrime = 0d;
|
||
|
} else if (Math.Abs(rf - max) < 0.0001) {
|
||
|
hPrime = ((gf - bf) / chroma) % 6;
|
||
|
} else if (Math.Abs(gf - max) < 0.0001) {
|
||
|
hPrime = 2 + (bf - rf) / chroma;
|
||
|
} else if (Math.Abs(bf - max) < 0.0001) {
|
||
|
hPrime = 4 + (rf - gf) / chroma;
|
||
|
}
|
||
|
|
||
|
var h = hPrime * 60f;
|
||
|
|
||
|
// determine lightness
|
||
|
var v = max;
|
||
|
|
||
|
// determine saturation
|
||
|
double s;
|
||
|
if (v is 0) {
|
||
|
s = 0d;
|
||
|
} else {
|
||
|
s = chroma / v;
|
||
|
}
|
||
|
|
||
|
return new Vector4((float) h, (float) s, (float) v, (float) af);
|
||
|
}
|
||
|
|
||
|
internal static double Luma(uint rgba) {
|
||
|
var (r, g, b, _) = ExplodeRgba(rgba);
|
||
|
return 0.2627 * r + 0.6780 * g + 0.0593 * b;
|
||
|
}
|
||
|
|
||
|
internal static (int, int, int) Step(uint rgba) {
|
||
|
var (r, g, b, _) = ExplodeRgba(rgba);
|
||
|
var lum = Math.Sqrt(0.241 * r + 0.691 * g + 0.068 * b);
|
||
|
var hsv = RgbaToHsv(rgba);
|
||
|
const int reps = 8;
|
||
|
var h2 = (int) (hsv.X * reps);
|
||
|
var lum2 = (int) (lum * reps);
|
||
|
var v2 = (int) (hsv.Z * reps);
|
||
|
|
||
|
if (h2 % 2 == 1) {
|
||
|
v2 = reps - v2;
|
||
|
lum2 = reps - lum2;
|
||
|
}
|
||
|
|
||
|
return (h2, lum2, v2);
|
||
|
}
|
||
|
}
|