1use clamp::clamp;
2use super::{Rgba, AsBytes};
3
4#[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}