1 module magicalrainbows.formats;
2 
3 import magicalrainbows.utils;
4 
5 import std.algorithm;
6 import std.bitmanip;
7 import std.conv;
8 import std.range;
9 
10 
11 struct BGR555 { //XBBBBBGG GGGRRRRR
12 	enum redSize = 5;
13 	enum greenSize = 5;
14 	enum blueSize = 5;
15 	enum alphaSize = 0;
16 	mixin colourConstructors;
17 	mixin(bitfields!(
18 		uint, "red", redSize,
19 		uint, "green", greenSize,
20 		uint, "blue", blueSize,
21 		bool, "padding", 1));
22 }
23 static assert(BGR555.sizeof == 2);
24 
25 @safe pure unittest {
26 	with(BGR555(AnalogRGBD(1.0, 0.5, 0.0))) {
27 		assert(red == 31);
28 		assert(green == 15);
29 		assert(blue == 0);
30 	}
31 }
32 
33 struct RGB555 { //XRRRRRGG GGGBBBBB
34 	enum redSize = 5;
35 	enum greenSize = 5;
36 	enum blueSize = 5;
37 	enum alphaSize = 0;
38 	mixin colourConstructors;
39 	mixin(bitfields!(
40 		uint, "blue", blueSize,
41 		uint, "green", greenSize,
42 		uint, "red", redSize,
43 		bool, "padding", 1));
44 }
45 static assert(RGB555.sizeof == 2);
46 
47 struct BGR565 { //BBBBBGGG GGGRRRRR
48 	enum redSize = 5;
49 	enum greenSize = 6;
50 	enum blueSize = 5;
51 	enum alphaSize = 0;
52 	mixin colourConstructors;
53 	mixin(bitfields!(
54 		uint, "red", redSize,
55 		uint, "green", greenSize,
56 		uint, "blue", blueSize));
57 }
58 static assert(BGR565.sizeof == 2);
59 
60 struct BGR222 { //00BBGGRR
61 	enum redSize = 2;
62 	enum greenSize = 2;
63 	enum blueSize = 2;
64 	enum alphaSize = 0;
65 	mixin colourConstructors;
66 	mixin(bitfields!(
67 		uint, "red", redSize,
68 		uint, "green", greenSize,
69 		uint, "blue", blueSize,
70 		ubyte, "padding", 2));
71 }
72 static assert(BGR222.sizeof == 1);
73 struct BGR333MD { //0000BBB0 GGG0RRR0
74 	enum redSize = 3;
75 	enum greenSize = 3;
76 	enum blueSize = 3;
77 	enum alphaSize = 0;
78 	mixin colourConstructors;
79 	mixin(bitfields!(
80 		ubyte, "padding0", 1,
81 		uint, "red", redSize,
82 		ubyte, "padding1", 1,
83 		uint, "green", greenSize,
84 		ubyte, "padding2", 1,
85 		uint, "blue", blueSize,
86 		ubyte, "padding3", 4));
87 }
88 static assert(BGR333MD.sizeof == 2);
89 
90 struct RGB888 { //RRRRRRRR GGGGGGGG BBBBBBBB
91 	enum redSize = 8;
92 	enum greenSize = 8;
93 	enum blueSize = 8;
94 	enum alphaSize = 0;
95 	mixin colourConstructors;
96 	ubyte red;
97 	ubyte green;
98 	ubyte blue;
99 }
100 static assert(RGB888.sizeof == 3);
101 
102 struct BGR888 { //BBBBBBBB GGGGGGGG RRRRRRRR
103 	enum redSize = 8;
104 	enum greenSize = 8;
105 	enum blueSize = 8;
106 	enum alphaSize = 0;
107 	mixin colourConstructors;
108 	ubyte blue;
109 	ubyte green;
110 	ubyte red;
111 }
112 static assert(BGR888.sizeof == 3);
113 
114 struct RGBA8888 { //RRRRRRRR GGGGGGGG BBBBBBBB AAAAAAAA
115 	enum redSize = 8;
116 	enum greenSize = 8;
117 	enum blueSize = 8;
118 	enum alphaSize = 8;
119 	mixin colourConstructors;
120 	ubyte red;
121 	ubyte green;
122 	ubyte blue;
123 	ubyte alpha;
124 }
125 static assert(RGBA8888.sizeof == 4);
126 @safe pure unittest {
127 	with(RGBA8888(AnalogRGBAD(1.0, 0.5, 0.0, 0.0))) {
128 		assert(red == 255);
129 		assert(green == 127);
130 		assert(blue == 0);
131 		assert(alpha == 0);
132 	}
133 	with(RGBA8888(255, 128, 0, 0)) {
134 		assert(red == 255);
135 		assert(green == 128);
136 		assert(blue == 0);
137 		assert(alpha == 0);
138 	}
139 }
140 struct BGRA8888 { // BBBBBBBB GGGGGGGG RRRRRRRR AAAAAAAA
141 	enum redSize = 8;
142 	enum greenSize = 8;
143 	enum blueSize = 8;
144 	enum alphaSize = 8;
145 	mixin colourConstructors;
146 	ubyte blue;
147 	ubyte green;
148 	ubyte red;
149 	ubyte alpha;
150 }
151 
152 static assert(BGRA8888.sizeof == 4);
153 
154 alias AnalogRGBF = AnalogRGB!float;
155 alias AnalogRGBD = AnalogRGB!double;
156 alias AnalogRGBR = AnalogRGB!real;
157 
158 struct AnalogRGB(Precision) {
159 	Precision red;
160 	Precision green;
161 	Precision blue;
162 }
163 
164 alias AnalogRGBAF = AnalogRGBA!float;
165 alias AnalogRGBAD = AnalogRGBA!double;
166 alias AnalogRGBAR = AnalogRGBA!real;
167 struct AnalogRGBA(Precision) {
168 	Precision red;
169 	Precision green;
170 	Precision blue;
171 	Precision alpha;
172 }
173 
174 alias HSVF = HSV!float;
175 alias HSVD = HSV!double;
176 alias HSVR = HSV!real;
177 struct HSV(Precision) {
178 	Precision hue;
179 	Precision saturation;
180 	Precision value;
181 	@safe invariant {
182 		assert(hue >= 0);
183 		assert(saturation >= 0);
184 		assert(value >= 0);
185 		assert(hue <= 1.0);
186 		assert(saturation <= 1.0);
187 		assert(value <= 1.0);
188 	}
189 }
190 struct HSVA(Precision) {
191 	Precision hue;
192 	Precision saturation;
193 	Precision value;
194 	Precision alpha;
195 	@safe invariant {
196 		assert(hue >= 0);
197 		assert(saturation >= 0);
198 		assert(value >= 0);
199 		assert(alpha >= 0);
200 		assert(hue <= 1.0);
201 		assert(saturation <= 1.0);
202 		assert(value <= 1.0);
203 		assert(alpha <= 1.0);
204 	}
205 }
206 
207 ///
208 HSV!Precision toHSV(Precision = double, Format)(Format input) if (isColourFormat!Format) {
209 	import std.algorithm.comparison : max, min;
210 	import std.math : isClose;
211 	HSV!Precision result;
212 	const red = input.redFP;
213 	const green = input.greenFP;
214 	const blue = input.blueFP;
215 	const minimum = min(red, green, blue);
216 	const maximum = max(red, green, blue);
217 	const delta = maximum - minimum;
218 
219 	result.value = maximum;
220 	if (delta < 0.00001)
221 	{
222 		result.saturation = 0;
223 		result.hue = 0;
224 		return result;
225 	}
226 	if (maximum > 0.0) {
227 		result.saturation = delta / maximum;
228 	}
229 
230 	if (isClose(red, maximum)) {
231 		result.hue = (green - blue) / delta; //yellow, magenta
232 	} else if (isClose(green, maximum)) {
233 		result.hue = 2.0 + (blue - red) / delta; //cyan, yellow
234 	} else {
235 		result.hue = 4.0 + (red - green) / delta; //magenta, cyan
236 	}
237 
238 	result.hue /= 6.0;
239 	if (result.hue < 0.0) {
240 		result.hue += 1.0;
241 	}
242 	assert(result.hue >= 0.0);
243 
244 	return result;
245 }
246 
247 ///
248 @safe unittest {
249 	import std.math : isClose;
250 	with(RGB888(0, 0, 0).toHSV) {
251 		assert(hue == 0);
252 		assert(saturation == 0);
253 		assert(value == 0);
254 	}
255 	with(RGB888(0, 128, 192).toHSV) {
256 		assert(hue.isClose(0.5555555555));
257 		assert(saturation.isClose(1.0));
258 		assert(value.isClose(0.7529411765));
259 	}
260 	with(RGB888(255, 255, 0).toHSV) {
261 		assert(hue.isClose(0.1666666667));
262 		assert(saturation.isClose(1.0));
263 		assert(value.isClose(1.0));
264 	}
265 	with(RGB888(255, 0, 0).toHSV) {
266 		assert(hue.isClose(0.0));
267 		assert(saturation.isClose(1.0));
268 		assert(value.isClose(1.0));
269 	}
270 	with(RGB888(255, 0, 255).toHSV) {
271 		assert(hue.isClose(0.8333333333));
272 		assert(saturation.isClose(1.0));
273 		assert(value.isClose(1.0));
274 	}
275 	with(RGB888(0, 255, 0).toHSV) {
276 		assert(hue.isClose(0.3333333333));
277 		assert(saturation.isClose(1.0));
278 		assert(value.isClose(1.0));
279 	}
280 	with(RGB888(0, 0, 255).toHSV) {
281 		assert(hue.isClose(0.6666666667));
282 		assert(saturation.isClose(1.0));
283 		assert(value.isClose(1.0));
284 	}
285 }
286 
287 ///
288 HSVA!Precision toHSVA(Precision = double, Format)(Format input) if (isColourFormat!Format) {
289 	const result = input.toHSV();
290 	static if (hasAlpha!Format) {
291 		return HSVA!Precision(result.hue, result.saturation, result.value, input.alphaFP);
292 	} else {
293 		return HSVA!Precision(result.hue, result.saturation, result.value, 1.0);
294 	}
295 }
296 
297 ///
298 @safe unittest {
299 	import std.math : isClose;
300 	with(RGB888(255, 0, 0).toHSVA) {
301 		assert(hue.isClose(0.0));
302 		assert(saturation.isClose(1.0));
303 		assert(value.isClose(1.0));
304 		assert(alpha.isClose(1.0));
305 	}
306 	with(RGBA8888(255, 0, 0, 255).toHSVA) {
307 		assert(hue.isClose(0.0));
308 		assert(saturation.isClose(1.0));
309 		assert(value.isClose(1.0));
310 		assert(alpha.isClose(1.0));
311 	}
312 	with(RGBA8888(255, 0, 0, 0).toHSVA) {
313 		assert(hue.isClose(0.0));
314 		assert(saturation.isClose(1.0));
315 		assert(value.isClose(1.0));
316 		assert(alpha.isClose(0.0));
317 	}
318 }
319 
320 ///
321 Format toRGB(Format = RGB888, Precision = double)(HSV!Precision input) @safe if (isColourFormat!Format) {
322 	static if (hasAlpha!Format) {
323 		alias AnalogRGBT = AnalogRGBA!Precision;
324 	} else {
325 		alias AnalogRGBT = AnalogRGB!Precision;
326 	}
327 
328 	if(input.saturation <= 0.0) {
329 		static if (hasAlpha!Format) {
330 			return Format(AnalogRGBT(input.value, input.value, input.value, 1.0));
331 		} else {
332 			return Format(AnalogRGBT(input.value, input.value, input.value));
333 		}
334 	}
335 	Precision hh = input.hue * 6.0;
336 	if(hh > 6.0) {
337 		hh	 = 0.0;
338 	}
339 	long i = cast(long)hh;
340 	Precision ff = hh - i;
341 	Precision p = input.value * (1.0 - input.saturation);
342 	Precision q = input.value * (1.0 - (input.saturation * ff));
343 	Precision t = input.value * (1.0 - (input.saturation * (1.0 - ff)));
344 
345 	assert(p <= 1.0);
346 	assert(q <= 1.0);
347 	assert(t <= 1.0);
348 	AnalogRGBT rgb;
349 	switch(i) {
350 		case 0:
351 			rgb = AnalogRGBT(input.value, t, p);
352 			break;
353 		case 1:
354 			rgb = AnalogRGBT(q, input.value, p);
355 			break;
356 		case 2:
357 			rgb = AnalogRGBT(p, input.value, t);
358 			break;
359 		case 3:
360 			rgb = AnalogRGBT(p, q, input.value);
361 			break;
362 		case 4:
363 			rgb = AnalogRGBT(t, p, input.value);
364 			break;
365 		case 5:
366 		default:
367 			rgb = AnalogRGBT(input.value, p, q);
368 			break;
369 	}
370 	static if (hasAlpha!Format) {
371 		rgb.alpha = 1.0;
372 	}
373 	return Format(rgb);
374 }
375 ///
376 @safe unittest {
377 	with(HSVD(0, 0, 0).toRGB!RGB888) {
378 		assert(red == 0);
379 		assert(green == 0);
380 		assert(blue == 0);
381 	}
382 	with(HSVD(0, 0, 0.5).toRGB!RGB888) {
383 		assert(red == 127);
384 		assert(green == 127);
385 		assert(blue == 127);
386 	}
387 	with(HSVD(0.5555555, 1.0, 0.752941).toRGB!RGB888) {
388 		assert(red == 0);
389 		assert(green == 128);
390 		assert(blue == 191);
391 	}
392 	with(HSVD(0.166666667, 1.0, 1.0).toRGB!RGB888) {
393 		assert(red == 254);
394 		assert(green == 255);
395 		assert(blue == 0);
396 	}
397 	with(HSVD(0.0, 1.0, 1.0).toRGB!RGB888) {
398 		assert(red == 255);
399 		assert(green == 0);
400 		assert(blue == 0);
401 	}
402 	with(HSVD(0.83333333, 1.0, 1.0).toRGB!RGB888) {
403 		assert(red == 254);
404 		assert(green == 0);
405 		assert(blue == 255);
406 	}
407 	with(HSVD(0.33333333, 1.0, 1.0).toRGB!RGB888) {
408 		assert(red == 0);
409 		assert(green == 255);
410 		assert(blue == 0);
411 	}
412 	with(HSVD(0.66666667, 1.0, 1.0).toRGB!RGB888) {
413 		assert(red == 0);
414 		assert(green == 0);
415 		assert(blue == 255);
416 	}
417 	with(HSVD(0.41666667, 1.0, 1.0).toRGB!RGB888) {
418 		assert(red == 0);
419 		assert(green == 255);
420 		assert(blue == 127);
421 	}
422 	with(HSVD(0.91666667, 1.0, 1.0).toRGB!RGB888) {
423 		assert(red == 255);
424 		assert(green == 0);
425 		assert(blue == 127);
426 	}
427 }
428 ///
429 Format toRGB(Format = RGB888, Precision = double)(HSVA!Precision input) @safe if (isColourFormat!Format) {
430 	Format result = HSV!Precision(input.hue, input.saturation, input.value).toRGB!Format();
431 	static if (hasAlpha!Format) {
432 		result.alphaFP = input.alpha;
433 	}
434 	return result;
435 }
436 ///
437 @safe unittest {
438 	with(HSVA!double(0, 0, 0.5, 0.0).toRGB!RGBA8888) {
439 		assert(red == 127);
440 		assert(green == 127);
441 		assert(blue == 127);
442 		assert(alpha == 0);
443 	}
444 	with(HSVA!double(0, 0, 0.5, 1.0).toRGB!RGBA8888) {
445 		assert(red == 127);
446 		assert(green == 127);
447 		assert(blue == 127);
448 		assert(alpha == 255);
449 	}
450 	with(HSVA!double(0, 0, 0.5, 1.0).toRGB!RGB888) {
451 		assert(red == 127);
452 		assert(green == 127);
453 		assert(blue == 127);
454 	}
455 }
456 
457 struct ColourPair(Foreground, Background) if (isColourFormat!Foreground && isColourFormat!Background) {
458 	Foreground foreground;
459 	Background background;
460 	Precision contrast(Precision = double)() const @safe pure {
461 		import magicalrainbows.properties : contrast;
462 		return contrast!Precision(foreground, background);
463 	}
464 	bool meetsWCAGAACriteria() const @safe pure {
465 		return contrast >= 4.5;
466 	}
467 	bool meetsWCAGAAACriteria() const @safe pure {
468 		return contrast >= 7.0;
469 	}
470 }
471 
472 ColourPair!(Foreground, Background) colourPair(Foreground, Background)(Foreground foreground, Background background) if (isColourFormat!Foreground && isColourFormat!Background) {
473 	return ColourPair!(Foreground, Background)(foreground, background);
474 }
475 ///
476 @safe pure unittest {
477 	import std.math : isClose;
478 	with(colourPair(RGB888(0, 0, 0), RGB888(255, 255, 255))) {
479 		assert(contrast.isClose(21.0));
480 		assert(meetsWCAGAACriteria);
481 		assert(meetsWCAGAAACriteria);
482 	}
483 	with(colourPair(RGB888(255, 255, 255), RGB888(255, 255, 255))) {
484 		assert(contrast.isClose(1.0));
485 		assert(!meetsWCAGAACriteria);
486 		assert(!meetsWCAGAAACriteria);
487 	}
488 	with(colourPair(RGB888(0, 128, 255), RGB888(0, 0, 0))) {
489 		assert(contrast.isClose(5.5316685936));
490 		assert(meetsWCAGAACriteria);
491 		assert(!meetsWCAGAAACriteria);
492 	}
493 	with(colourPair(BGR555(0, 16, 31), RGB888(0, 0, 0))) {
494 		assert(contrast.isClose(5.7235463090));
495 		assert(meetsWCAGAACriteria);
496 		assert(!meetsWCAGAAACriteria);
497 	}
498 }
499 
500 Target convert(Target, Source)(Source from) if (isColourFormat!Source && isColourFormat!Target) {
501 	static if (is(Target == Source)) {
502 		return from;
503 	} else {
504 		Target output;
505 		static if (hasRed!Source && hasRed!Target) {
506 			output.red = colourConvert!(typeof(output.red), Target.redSize, Source.redSize)(from.red);
507 		}
508 		static if (hasGreen!Source && hasGreen!Target) {
509 			output.green = colourConvert!(typeof(output.green), Target.greenSize, Source.greenSize)(from.green);
510 		}
511 		static if (hasBlue!Source && hasBlue!Target) {
512 			output.blue = colourConvert!(typeof(output.blue), Target.blueSize, Source.blueSize)(from.blue);
513 		}
514 		static if (hasAlpha!Source && hasAlpha!Target) {
515 			output.alpha = colourConvert!(typeof(output.alpha), Target.alphaSize, Source.alphaSize)(from.alpha);
516 		} else static if (hasAlpha!Target) {
517 			output.alpha = output.alpha.max;
518 		}
519 		return output;
520 	}
521 }
522 ///
523 @safe pure unittest {
524 	assert(BGR555(31,31,31).convert!RGB888 == RGB888(248, 248, 248));
525 	assert(BGR555(0, 0, 0).convert!RGB888 == RGB888(0, 0, 0));
526 	assert(RGB888(248, 248, 248).convert!BGR555 == BGR555(31,31,31));
527 	assert(RGB888(0, 0, 0).convert!BGR555 == BGR555(0, 0, 0));
528 }
529 
530 Format fromHex(Format = RGB888)(const string colour) @safe pure if (isColourFormat!Format) {
531 	Format output;
532 	string tmpStr = colour[];
533 	if (colour.empty) {
534 		throw new Exception("Cannot parse an empty string");
535 	}
536 	if (tmpStr.front == '#') {
537 		tmpStr.popFront();
538 	}
539 	enum alphaAdjustment = hasAlpha!Format ? 1 : 0;
540 	if (tmpStr.length == 3 + alphaAdjustment) {
541 		auto tmp = tmpStr[0].repeat(2);
542 		output.red = tmp.parse!ubyte(16);
543 		tmp = tmpStr[1].repeat(2);
544 		output.green = tmp.parse!ubyte(16);
545 		tmp = tmpStr[2].repeat(2);
546 		output.blue = tmp.parse!ubyte(16);
547 		static if (hasAlpha!Format) {
548 			tmp = tmpStr[3].repeat(2);
549 			output.alpha = tmp.parse!ubyte(16);
550 		}
551 	} else if (tmpStr.length == (3 + alphaAdjustment) * 2) {
552 		auto tmp = tmpStr[0..2];
553 		output.red = tmp.parse!ubyte(16);
554 		tmp = tmpStr[2..4];
555 		output.green = tmp.parse!ubyte(16);
556 		tmp = tmpStr[4..6];
557 		output.blue = tmp.parse!ubyte(16);
558 		static if (hasAlpha!Format) {
559 			tmp = tmpStr[6..8];
560 			output.alpha = tmp.parse!ubyte(16);
561 		}
562 	}
563 	return output;
564 }
565 ///
566 @safe pure unittest {
567 	assert("#000000".fromHex == RGB888(0, 0, 0));
568 	assert("#FFFFFF".fromHex == RGB888(255, 255, 255));
569 	assert("FFFFFF".fromHex == RGB888(255, 255, 255));
570 	assert("#FFF".fromHex == RGB888(255, 255, 255));
571 	assert("#888".fromHex == RGB888(0x88, 0x88, 0x88));
572 	assert("888".fromHex == RGB888(0x88, 0x88, 0x88));
573 	assert("#FFFFFFFF".fromHex!RGBA8888 == RGBA8888(255, 255, 255, 255));
574 	assert("#FFFF".fromHex!RGBA8888 == RGBA8888(255, 255, 255, 255));
575 }
576 
577 //TODO: can we express these with fractions instead?
578 
579 //Assumptions:
580 // R,G,B,Y [0, 255]
581 // Pb,Pr [-127.5, 127.5]
582 enum YPbPrSDTVToRGBMatrix = [
583 	[1.0, 0.0, 1.402],
584 	[1.0, -0.344, -0.714],
585 	[1.0, 1.772, 0.0],
586 ];
587 //Assumptions:
588 // R,G,B,Y [0, 255]
589 // Pb,Pr [-127.5, 127.5]
590 enum YPbPrHDTVToRGBMatrix = [
591 	[1.0, 0.0, 1.575],
592 	[1.0, -0.187, -0.468],
593 	[1.0, 1.856, 0.0],
594 ];
595 
596 //Assumptions:
597 // R,G,B,Y [0, 255]
598 // Pb,Pr [-127.5, 127.5]
599 enum RGBToYPbPrSDTVMatrix = [
600 	[0.299, 0.587, 0.114],
601 	[-0.169, -0.331, 0.5],
602 	[0.5, -0.419, -0.081],
603 ];
604 
605 //Assumptions:
606 // R,G,B,Y [0, 255]
607 // Pb,Pr [-127.5, 127.5]
608 enum RGBToYPbPrHDTVMatrix = [
609 	[0.213, 0.715, 0.072],
610 	[-0.115, -0.385, 0.5],
611 	[0.5, -0.454, -0.046],
612 ];
613 
614 struct YPbPr {
615 	double Y;
616 	double Pb;
617 	double Pr;
618 }
619 
620 //Assumptions:
621 // R,G,B,Y [0, 1]
622 // U [-0.436, 0.436]
623 // V [-0.615, 0.615]
624 enum RGBToYUVMatrix = [
625 	[0.299, 0.587, 0.114],
626 	[-0.147, -0.289, 0.436],
627 	[0.615, -0.515, -0.100]
628 ];
629 
630 //Assumptions:
631 // R,G,B,Y [0, 1]
632 // U [-0.436, 0.436]
633 // V [-0.615, 0.615]
634 enum YUVToRGBMatrix = [
635 	[1.0, 0.0, 1.140],
636 	[1.0, -0.395, -0.581],
637 	[1.0, 2.032, 0.0],
638 ];
639 
640 struct YUV {
641 	double Y;
642 	double Cb;
643 	double Cr;
644 }
645 
646 enum YCbCrSDTVVector = [[16], [128], [128]];
647 alias YCbCrHDTVVector = YCbCrSDTVVector;
648 enum YCbCrFullRangeVector = [[0], [128], [128]];
649 
650 //Assumptions:
651 // R,G,B [0, 255]
652 // Y [16, 235]
653 // Cb,Cr [16, 240]
654 enum YCbCrSDTVToRGBMatrix = [
655 	[1.164, 0.0, 1.596],
656 	[1.164, -0.392, -0.813],
657 	[1.164, 2.017, 0.0],
658 ];
659 
660 //Assumptions:
661 // R,G,B [0, 255]
662 // Y [16, 235]
663 // Cb,Cr [16, 240]
664 enum YCbCrHDTVToRGBMatrix = [
665 	[1.164, 0.0, 1.793],
666 	[1.164, -0.213, -0.533],
667 	[1.164, 2.112, 0.0],
668 ];
669 
670 //Assumptions:
671 // R,G,B,Y,Cb,Cr [0, 255]
672 enum YCbCrFullRangeToRGBMatrix = [
673 	[1.0, 0.0, 1.4],
674 	[1.0, -0.343, -0.711],
675 	[1.0, 1.765, 0.0],
676 ];
677 
678 //Assumptions:
679 // R,G,B [0, 255]
680 // Y [16, 235]
681 // Cb,Cr [16, 240]
682 enum RGBToYCbCrSDTVMatrix = [
683 	[0.257, 0.504, 0.098],
684 	[-0.148, -0.291, 0.439],
685 	[0.439, -0.368, -0.071],
686 ];
687 
688 //Assumptions:
689 // R,G,B [0, 255]
690 // Y [16, 235]
691 // Cb,Cr [16, 240]
692 enum RGBToYCbCrHDTVMatrix = [
693 	[0.183, 0.614, 0.062],
694 	[-0.101, -0.339, 0.439],
695 	[0.439, -0.399, -0.040],
696 ];
697 
698 //Assumptions:
699 // R,G,B,Y,Cb,Cr [0, 255]
700 enum RGBToYCbCrFullRangeMatrix = [
701 	[0.299, 0.587, 0.114],
702 	[-0.169, -0.331, 0.500],
703 	[0.500, -0.419, -0.081],
704 ];
705 
706 struct YCbCr {
707 	double Y;
708 	double Cb;
709 	double Cr;
710 }