framing/formats/
yuv.rs

1use clamp::clamp;
2use super::{Rgba, AsBytes};
3
4/// A three-byte long pixel in the Y'CbCr format.
5///
6/// Conversion to and from RGBA follows the BT.709 standard for 8-bit digital
7/// representation of Y'CbCr encoded pixels.
8#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
9pub struct Yuv<T = u8>(pub T, pub T, pub T);
10
11unsafe impl AsBytes for Yuv {
12    type Bytes = [u8; 3];
13    fn width() -> usize { 3 }
14}
15
16impl From<[u8; 3]> for Yuv {
17    fn from(c: [u8; 3]) -> Self {
18        Yuv(c[0], c[1], c[2])
19    }
20}
21
22impl From<Yuv> for [u8; 3] {
23    fn from(p: Yuv) -> Self {
24        [p.0, p.1, p.2]
25    }
26}
27
28impl From<Rgba> for Yuv {
29    fn from(Rgba(r, g, b, _): Rgba) -> Yuv {
30        let (r, g, b) = (r as f32, g as f32, b as f32);
31
32        let y =   0.183 * r + 0.614 * g + 0.062 * b;
33        let u = - 0.101 * r - 0.339 * g + 0.439 * b;
34        let v =   0.439 * r - 0.399 * g - 0.040 * b;
35
36        Yuv(
37            (y +  16.0) as u8,
38            (u + 128.0) as u8,
39            (v + 128.0) as u8
40        )
41    }
42}
43
44impl From<Yuv> for Rgba {
45    fn from(Yuv(y, u, v): Yuv) -> Rgba {
46        let (y, u, v) = (y as f32 - 16.0, u as f32 - 128.0, v as f32 - 128.0);
47
48        let r = clamp(1.164 * y             + 1.793 * v, 0.0, 255.0);
49        let g = clamp(1.164 * y - 0.213 * u - 0.533 * v, 0.0, 255.0);
50        let b = clamp(1.164 * y + 2.112 * u            , 0.0, 255.0);
51
52        Rgba(
53            r as u8,
54            g as u8,
55            b as u8,
56            255
57        )
58    }
59}
60
61#[test]
62fn conversion() {
63    const MAX_ERROR: u8 = 4;
64
65    for r in 0..255 {
66    for g in 0..255 {
67    for b in 0..255 {
68        let a = Rgba(r, g, b, 255);
69        let b = Rgba::from(Yuv::from(a));
70
71        let distchk = |x, y| {
72            let dist = if y <= x { x - y } else { y - x };
73
74            if dist > MAX_ERROR {
75                panic!("{:?} is too far from {:?}!", b, a);
76            }
77        };
78
79        distchk(a.0, b.0);
80        distchk(a.1, b.1);
81        distchk(a.2, b.2);
82        assert_eq!(b.3, 255);
83    }}}
84}