| 1 |
using System; |
| 2 |
using System.Globalization; |
| 3 |
|
| 4 |
namespace Oni.Imaging |
| 5 |
{ |
| 6 |
internal struct Color : IEquatable<Color> |
| 7 |
{ |
| 8 |
private byte b, g, r, a; |
| 9 |
|
| 10 |
public Color(byte r, byte g, byte b) |
| 11 |
: this(r, g, b, 255) |
| 12 |
{ |
| 13 |
} |
| 14 |
|
| 15 |
public Color(byte r, byte g, byte b, byte a) |
| 16 |
{ |
| 17 |
this.b = b; |
| 18 |
this.g = g; |
| 19 |
this.r = r; |
| 20 |
this.a = a; |
| 21 |
} |
| 22 |
|
| 23 |
public Color(Vector3 v) |
| 24 |
{ |
| 25 |
r = (byte)(v.X * 255.0f); |
| 26 |
g = (byte)(v.Y * 255.0f); |
| 27 |
b = (byte)(v.Z * 255.0f); |
| 28 |
a = 255; |
| 29 |
} |
| 30 |
|
| 31 |
public Color(Vector4 v) |
| 32 |
{ |
| 33 |
r = (byte)(v.X * 255.0f); |
| 34 |
g = (byte)(v.Y * 255.0f); |
| 35 |
b = (byte)(v.Z * 255.0f); |
| 36 |
a = (byte)(v.W * 255.0f); |
| 37 |
} |
| 38 |
|
| 39 |
public bool IsTransparent => a != 255; |
| 40 |
|
| 41 |
public byte R => r; |
| 42 |
public byte G => g; |
| 43 |
public byte B => b; |
| 44 |
public byte A => a; |
| 45 |
|
| 46 |
public int ToBgra32() => b | (g << 8) | (r << 16) | (a << 24); |
| 47 |
public int ToBgr565() => (b >> 3) | ((g & 0xfc) << 3) | ((r & 0xf8) << 8); |
| 48 |
|
| 49 |
public Vector3 ToVector3() => new Vector3(r, g, b) / 255.0f; |
| 50 |
public Vector4 ToVector4() => new Vector4(r, g, b, a) / 255.0f; |
| 51 |
|
| 52 |
public static bool operator ==(Color a, Color b) => a.Equals(b); |
| 53 |
public static bool operator !=(Color a, Color b) => !a.Equals(b); |
| 54 |
|
| 55 |
public bool Equals(Color color) => r == color.r && g == color.g && b == color.b && a == color.a; |
| 56 |
public override bool Equals(object obj) => obj is Color && Equals((Color)obj); |
| 57 |
public override int GetHashCode() => r.GetHashCode() ^ g.GetHashCode() ^ b.GetHashCode() ^ a.GetHashCode(); |
| 58 |
|
| 59 |
public override string ToString() => string.Format(CultureInfo.InvariantCulture, "{{R:{0} G:{1} B:{2} A:{3}}}", new object[] { r, g, b, a }); |
| 60 |
|
| 61 |
public static Color ReadBgra4444(byte[] data, int index) |
| 62 |
{ |
| 63 |
int pixel = data[index + 0]; |
| 64 |
byte b = (byte)((pixel << 4) & 0xf0); |
| 65 |
byte g = (byte)(pixel & 0xf0); |
| 66 |
pixel = data[index + 1]; |
| 67 |
byte r = (byte)((pixel << 4) & 0xf0); |
| 68 |
byte a = (byte)(pixel & 0xf0); |
| 69 |
|
| 70 |
return new Color(r, g, b, a); |
| 71 |
} |
| 72 |
|
| 73 |
public static void WriteBgra4444(Color color, byte[] data, int index) |
| 74 |
{ |
| 75 |
data[index + 0] = (byte)((color.b >> 4) | (color.g & 0xf0)); |
| 76 |
data[index + 1] = (byte)((color.r >> 4) | (color.a & 0xf0)); |
| 77 |
} |
| 78 |
|
| 79 |
public static Color ReadBgrx5551(byte[] data, int index) |
| 80 |
{ |
| 81 |
int pixel = data[index + 0] | (data[index + 1] << 8); |
| 82 |
byte b = (byte)((pixel << 3) & 0xf8); |
| 83 |
byte g = (byte)((pixel >> 2) & 0xf8); |
| 84 |
byte r = (byte)((pixel >> 7) & 0xf8); |
| 85 |
|
| 86 |
return new Color(r, g, b); |
| 87 |
} |
| 88 |
|
| 89 |
public static void WriteBgrx5551(Color color, byte[] data, int index) |
| 90 |
{ |
| 91 |
data[index + 0] = (byte)((color.b >> 3) | ((color.g & 0x38) << 2)); |
| 92 |
data[index + 1] = (byte)((color.g >> 6) | ((color.r & 0xf8) >> 1) | 0x80); |
| 93 |
} |
| 94 |
|
| 95 |
public static Color ReadBgra5551(byte[] data, int index) |
| 96 |
{ |
| 97 |
int pixel = data[index + 0] | (data[index + 1] << 8); |
| 98 |
byte b = (byte)((pixel << 3) & 0xf8); |
| 99 |
byte g = (byte)((pixel >> 2) & 0xf8); |
| 100 |
byte r = (byte)((pixel >> 7) & 0xf8); |
| 101 |
byte a = (byte)((pixel >> 15) * 255); |
| 102 |
|
| 103 |
return new Color(r, g, b, a); |
| 104 |
} |
| 105 |
|
| 106 |
public static void WriteBgra5551(Color color, byte[] data, int index) |
| 107 |
{ |
| 108 |
data[index + 0] = (byte)((color.b >> 3) | ((color.g & 0x38) << 2)); |
| 109 |
data[index + 1] = (byte)((color.g >> 6) | ((color.r & 0xf8) >> 1) | (color.a & 0x80)); |
| 110 |
} |
| 111 |
|
| 112 |
public static Color ReadBgr565(byte[] data, int index) |
| 113 |
{ |
| 114 |
int pixel = data[index + 0] | (data[index + 1] << 8); |
| 115 |
byte b = (byte)((pixel << 3) & 0xf8); |
| 116 |
byte g = (byte)((pixel >> 3) & 0xfc); |
| 117 |
byte r = (byte)((pixel >> 8) & 0xf8); |
| 118 |
|
| 119 |
return new Color(r, g, b); |
| 120 |
} |
| 121 |
|
| 122 |
public static void WriteBgr565(Color color, byte[] data, int index) |
| 123 |
{ |
| 124 |
data[index + 0] = (byte)((color.b >> 3) | ((color.g & 0x1C) << 3)); |
| 125 |
data[index + 1] = (byte)((color.g >> 5) | (color.r & 0xf8)); |
| 126 |
} |
| 127 |
|
| 128 |
public static Color ReadBgrx(byte[] data, int index) |
| 129 |
{ |
| 130 |
byte b = data[index + 0]; |
| 131 |
byte g = data[index + 1]; |
| 132 |
byte r = data[index + 2]; |
| 133 |
|
| 134 |
return new Color(r, g, b); |
| 135 |
} |
| 136 |
|
| 137 |
public static void WriteBgrx(Color color, byte[] data, int index) |
| 138 |
{ |
| 139 |
data[index + 0] = color.b; |
| 140 |
data[index + 1] = color.g; |
| 141 |
data[index + 2] = color.r; |
| 142 |
data[index + 3] = 255; |
| 143 |
} |
| 144 |
|
| 145 |
public static Color ReadBgra(byte[] data, int index) |
| 146 |
{ |
| 147 |
byte b = data[index + 0]; |
| 148 |
byte g = data[index + 1]; |
| 149 |
byte r = data[index + 2]; |
| 150 |
byte a = data[index + 3]; |
| 151 |
|
| 152 |
return new Color(r, g, b, a); |
| 153 |
} |
| 154 |
|
| 155 |
public static void WriteBgra(Color color, byte[] data, int index) |
| 156 |
{ |
| 157 |
data[index + 0] = color.b; |
| 158 |
data[index + 1] = color.g; |
| 159 |
data[index + 2] = color.r; |
| 160 |
data[index + 3] = color.a; |
| 161 |
} |
| 162 |
|
| 163 |
public static Color ReadRgbx(byte[] data, int index) |
| 164 |
{ |
| 165 |
byte r = data[index + 0]; |
| 166 |
byte g = data[index + 1]; |
| 167 |
byte b = data[index + 2]; |
| 168 |
|
| 169 |
return new Color(r, g, b); |
| 170 |
} |
| 171 |
|
| 172 |
public static void WriteRgbx(Color color, byte[] data, int index) |
| 173 |
{ |
| 174 |
data[index + 0] = color.r; |
| 175 |
data[index + 1] = color.g; |
| 176 |
data[index + 2] = color.b; |
| 177 |
data[index + 3] = 255; |
| 178 |
} |
| 179 |
|
| 180 |
public static Color ReadRgba(byte[] data, int index) |
| 181 |
{ |
| 182 |
byte r = data[index + 0]; |
| 183 |
byte g = data[index + 1]; |
| 184 |
byte b = data[index + 2]; |
| 185 |
byte a = data[index + 3]; |
| 186 |
|
| 187 |
return new Color(r, g, b, a); |
| 188 |
} |
| 189 |
|
| 190 |
public static void WriteRgba(Color color, byte[] data, int index) |
| 191 |
{ |
| 192 |
data[index + 0] = color.r; |
| 193 |
data[index + 1] = color.g; |
| 194 |
data[index + 2] = color.b; |
| 195 |
data[index + 3] = color.a; |
| 196 |
} |
| 197 |
|
| 198 |
public static Color Lerp(Color x, Color y, float amount) |
| 199 |
{ |
| 200 |
byte r = (byte)MathHelper.Lerp(x.r, y.r, amount); |
| 201 |
byte g = (byte)MathHelper.Lerp(x.g, y.g, amount); |
| 202 |
byte b = (byte)MathHelper.Lerp(x.b, y.b, amount); |
| 203 |
byte a = (byte)MathHelper.Lerp(x.a, y.a, amount); |
| 204 |
|
| 205 |
return new Color(r, g, b, a); |
| 206 |
} |
| 207 |
|
| 208 |
private static readonly Color black = new Color(0, 0, 0, 255); |
| 209 |
private static readonly Color white = new Color(255, 255, 255, 255); |
| 210 |
private static readonly Color transparent = new Color(0, 0, 0, 0); |
| 211 |
|
| 212 |
public static Color White => white; |
| 213 |
public static Color Black => black; |
| 214 |
public static Color Transparent => transparent; |
| 215 |
|
| 216 |
public static bool TryParse(string s, out Color color) |
| 217 |
{ |
| 218 |
color = new Color(); |
| 219 |
color.a = 255; |
| 220 |
|
| 221 |
if (string.IsNullOrEmpty(s)) |
| 222 |
return false; |
| 223 |
|
| 224 |
var tokens = s.Split(new char[0], StringSplitOptions.RemoveEmptyEntries); |
| 225 |
|
| 226 |
if (tokens.Length < 3 || tokens.Length > 4) |
| 227 |
return false; |
| 228 |
|
| 229 |
if (!byte.TryParse(tokens[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out color.r)) |
| 230 |
return false; |
| 231 |
|
| 232 |
if (!byte.TryParse(tokens[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out color.g)) |
| 233 |
return false; |
| 234 |
|
| 235 |
if (!byte.TryParse(tokens[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out color.b)) |
| 236 |
return false; |
| 237 |
|
| 238 |
if (tokens.Length > 3 && !byte.TryParse(tokens[3], NumberStyles.Integer, CultureInfo.InvariantCulture, out color.a)) |
| 239 |
return false; |
| 240 |
|
| 241 |
return true; |
| 242 |
} |
| 243 |
|
| 244 |
public static Color Parse(string s) |
| 245 |
{ |
| 246 |
Color c; |
| 247 |
|
| 248 |
if (!TryParse(s, out c)) |
| 249 |
throw new FormatException(string.Format("'{0}' is not a color", s)); |
| 250 |
|
| 251 |
return c; |
| 252 |
} |
| 253 |
} |
| 254 |
} |