Nerd_STF/Nerd_STF/Graphics/RGBAByte.cs

221 lines
9.7 KiB
C#

namespace Nerd_STF.Graphics;
public struct RGBAByte : IColorByte, IEquatable<RGBAByte>
{
public static RGBAByte Black => new(0, 0, 0);
public static RGBAByte Blue => new(0, 0, 255);
public static RGBAByte Clear => new(0, 0, 0, 0);
public static RGBAByte Cyan => new(0, 255, 255);
public static RGBAByte Gray => new(127, 127, 127);
public static RGBAByte Green => new(0, 255, 0);
public static RGBAByte Magenta => new(255, 0, 255);
public static RGBAByte Orange => new(255, 127, 0);
public static RGBAByte Purple => new(127, 0, 255);
public static RGBAByte Red => new(255, 0, 0);
public static RGBAByte White => new(255, 255, 255);
public static RGBAByte Yellow => new(255, 255, 0);
public byte R, G, B, A;
public bool HasBlue => B > 0;
public bool HasGreen => G > 0;
public bool HasRed => R > 0;
public bool IsOpaque => A == 255;
public bool IsVisible => A != 0;
public RGBAByte() : this(0, 0, 0, 255) { }
public RGBAByte(int all) : this(all, all, all, all) { }
public RGBAByte(int all, int a) : this(all, all, all, a) { }
public RGBAByte(int r, int g, int b) : this(r, g, b, 255) { }
public RGBAByte(int r, int g, int b, int a)
{
R = (byte)Mathf.Clamp(r, 0, 255);
G = (byte)Mathf.Clamp(g, 0, 255);
B = (byte)Mathf.Clamp(b, 0, 255);
A = (byte)Mathf.Clamp(a, 0, 255);
}
public RGBAByte(Fill<byte> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public RGBAByte(Fill<int> fill) : this(fill(0), fill(1), fill(2), fill(3)) { }
public byte this[int index]
{
get => index switch
{
0 => R,
1 => G,
2 => B,
3 => A,
_ => throw new IndexOutOfRangeException(nameof(index)),
};
set
{
switch (index)
{
case 0:
R = value;
break;
case 1:
G = value;
break;
case 2:
B = value;
break;
case 3:
A = value;
break;
default: throw new IndexOutOfRangeException(nameof(index));
}
}
}
public static RGBAByte Average(params RGBAByte[] vals)
{
RGBAByte val = new(0, 0, 0, 0);
for (int i = 0; i < vals.Length; i++) val += vals[i];
return val / vals.Length;
}
public static RGBAByte Clamp(RGBAByte val, RGBAByte min, RGBAByte max) =>
new(Mathf.Clamp(val.R, min.R, max.R),
Mathf.Clamp(val.G, min.G, max.G),
Mathf.Clamp(val.B, min.B, max.B),
Mathf.Clamp(val.A, min.A, max.A));
public static RGBAByte Lerp(RGBAByte a, RGBAByte b, byte t, bool clamp = true) =>
new(Mathf.Lerp(a.R, b.R, t, clamp), Mathf.Lerp(a.G, b.G, t, clamp), Mathf.Lerp(a.B, b.B, t, clamp),
Mathf.Lerp(a.A, b.A, t, clamp));
public static RGBAByte LerpSquared(RGBAByte a, RGBAByte b, byte t, bool clamp = true) =>
RGBA.LerpSquared(a.ToRGBA(), b.ToRGBA(), t, clamp).ToRGBAByte();
public static RGBAByte Median(params RGBAByte[] vals)
{
float index = Mathf.Average(0, vals.Length - 1);
RGBAByte valA = vals[Mathf.Floor(index)], valB = vals[Mathf.Ceiling(index)];
return Average(valA, valB);
}
public static RGBAByte Max(params RGBAByte[] vals)
{
(int[] Rs, int[] Gs, int[] Bs, int[] As) = SplitArrayInt(vals);
return new(Mathf.Max(Rs), Mathf.Max(Gs), Mathf.Max(Bs), Mathf.Max(As));
}
public static RGBAByte Min(params RGBAByte[] vals)
{
(int[] Rs, int[] Gs, int[] Bs, int[] As) = SplitArrayInt(vals);
return new(Mathf.Min(Rs), Mathf.Min(Gs), Mathf.Min(Bs), Mathf.Min(As));
}
public static (byte[] Rs, byte[] Gs, byte[] Bs, byte[] As) SplitArray(params RGBAByte[] vals)
{
byte[] Rs = new byte[vals.Length], Gs = new byte[vals.Length],
Bs = new byte[vals.Length], As = new byte[vals.Length];
for (int i = 0; i < vals.Length; i++)
{
Rs[i] = vals[i].R;
Gs[i] = vals[i].G;
Bs[i] = vals[i].B;
As[i] = vals[i].A;
}
return (Rs, Gs, Bs, As);
}
public static (int[] Rs, int[] Gs, int[] Bs, int[] As) SplitArrayInt(params RGBAByte[] vals)
{
int[] Rs = new int[vals.Length], Gs = new int[vals.Length],
Bs = new int[vals.Length], As = new int[vals.Length];
for (int i = 0; i < vals.Length; i++)
{
Rs[i] = vals[i].R;
Gs[i] = vals[i].G;
Bs[i] = vals[i].B;
As[i] = vals[i].A;
}
return (Rs, Gs, Bs, As);
}
public bool Equals(IColorFloat? col) => col != null && Equals(col.ToRGBAByte());
public bool Equals(IColorByte? col) => col != null && Equals(col.ToRGBAByte());
public bool Equals(RGBAByte col) => A == 0 && col.A == 0 || R == col.R && G == col.G && B == col.B && A == col.A;
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (obj == null) return base.Equals(obj);
Type t = obj.GetType();
if (t == typeof(RGBAByte)) return Equals((RGBAByte)obj);
else if (t == typeof(CMYKA)) return Equals((IColorFloat)obj);
else if (t == typeof(HSVA)) return Equals((IColorFloat)obj);
else if (t == typeof(IColorFloat)) return Equals((IColorFloat)obj);
else if (t == typeof(RGBA)) return Equals((IColorByte)obj);
else if (t == typeof(CMYKAByte)) return Equals((IColorByte)obj);
else if (t == typeof(HSVAByte)) return Equals((IColorByte)obj);
else if (t == typeof(IColorByte)) return Equals((IColorByte)obj);
return base.Equals(obj);
}
public override int GetHashCode() => R.GetHashCode() ^ G.GetHashCode() ^ B.GetHashCode() ^ A.GetHashCode();
public string ToString(IFormatProvider provider) => "R: " + R.ToString(provider) + " G: " + G.ToString(provider) +
" B: " + B.ToString(provider) + " A: " + A.ToString(provider);
public string ToString(string? provider) => "R: " + R.ToString(provider) + " G: " + G.ToString(provider) +
" B: " + B.ToString(provider) + " A: " + A.ToString(provider);
public override string ToString() => ToString((string?)null);
public RGBA ToRGBA() => new(R / 255f, G / 255f, B / 255f, A / 255f);
public CMYKA ToCMYKA() => ToRGBA().ToCMYKA();
public HSVA ToHSVA() => ToRGBA().ToHSVA();
public RGBAByte ToRGBAByte() => this;
public CMYKAByte ToCMYKAByte() => ToRGBA().ToCMYKAByte();
public HSVAByte ToHSVAByte() => ToRGBA().ToHSVAByte();
public byte[] ToArray() => new[] { R, G, B, A };
public Fill<byte> ToFill()
{
HSVAByte @this = this;
return i => @this[i];
}
public List<byte> ToList() => new() { R, G, B, A };
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<byte> GetEnumerator()
{
yield return R;
yield return G;
yield return B;
yield return A;
}
public object Clone() => new RGBAByte(R, G, B, A);
public Vector3d ToVector() => ((RGBA)this).ToVector();
public static RGBAByte operator +(RGBAByte a, RGBAByte b) => new(a.R + b.R, a.G + b.G, a.B + b.B, a.A + b.A);
public static RGBAByte operator -(RGBAByte c) => new(255 - c.R, 255 - c.G, 255 - c.B, c.A != 255 ? 255 - c.A : 255);
public static RGBAByte operator -(RGBAByte a, RGBAByte b) => new(a.R - b.R, a.G - b.G, a.B - b.B, a.A - b.A);
public static RGBAByte operator *(RGBAByte a, RGBAByte b) => new(a.R * b.R, a.G * b.G, a.B * b.B, a.A * b.A);
public static RGBAByte operator *(RGBAByte a, int b) => new(a.R * b, a.G * b, a.B * b, a.A * b);
public static RGBAByte operator *(RGBAByte a, float b) => (a.ToRGBA() * b).ToRGBAByte();
public static RGBAByte operator /(RGBAByte a, RGBAByte b) => new(a.R / b.R, a.G / b.G, a.B / b.B, a.A / b.A);
public static RGBAByte operator /(RGBAByte a, int b) => new(a.R / b, a.G / b, a.B / b, a.A / b);
public static RGBAByte operator /(RGBAByte a, float b) => (a.ToRGBA() / b).ToRGBAByte();
public static bool operator ==(RGBAByte a, RGBA b) => a.Equals((IColorFloat?)b);
public static bool operator !=(RGBAByte a, RGBA b) => !a.Equals((IColorFloat?)b);
public static bool operator ==(RGBAByte a, CMYKA b) => a.Equals((IColorFloat?)b);
public static bool operator !=(RGBAByte a, CMYKA b) => !a.Equals((IColorFloat?)b);
public static bool operator ==(RGBAByte a, HSVA b) => a.Equals((IColorFloat?)b);
public static bool operator !=(RGBAByte a, HSVA b) => !a.Equals((IColorFloat?)b);
public static bool operator ==(RGBAByte a, RGBAByte b) => a.Equals(b);
public static bool operator !=(RGBAByte a, RGBAByte b) => !a.Equals(b);
public static bool operator ==(RGBAByte a, CMYKAByte b) => a.Equals(b);
public static bool operator !=(RGBAByte a, CMYKAByte b) => !a.Equals(b);
public static bool operator ==(RGBAByte a, HSVAByte b) => a.Equals(b);
public static bool operator !=(RGBAByte a, HSVAByte b) => !a.Equals(b);
public static implicit operator RGBAByte(Int3 val) => new(val.x, val.y, val.z);
public static implicit operator RGBAByte(Int4 val) => new(val.x, val.y, val.z, val.w);
public static implicit operator RGBAByte(CMYKA val) => val.ToRGBAByte();
public static implicit operator RGBAByte(HSVA val) => val.ToRGBAByte();
public static implicit operator RGBAByte(RGBA val) => val.ToRGBAByte();
public static implicit operator RGBAByte(CMYKAByte val) => val.ToRGBAByte();
public static implicit operator RGBAByte(HSVAByte val) => val.ToRGBAByte();
public static implicit operator RGBAByte(Fill<byte> val) => new(val);
public static implicit operator RGBAByte(Fill<int> val) => new(val);
}