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 }