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 |
} |