pub const EOTF_PQ: u8 = 2;
pub const EOTF_HLG: u8 = 3;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Hdr {
pub eotf: u8,
pub max_luma: u16,
pub min_luma: u16,
pub max_cll: u16,
pub max_fall: u16,
}
impl Hdr {
pub fn blob(&self) -> [u8; 32] {
let mut b = [0u8; 32];
b[4] = self.eotf;
let words: [u16; 12] = [
8500, 39850, 6550, 2300, 35400, 14600, 15635, 16450,
self.max_luma, self.min_luma, self.max_cll, self.max_fall,
];
for (i, w) in words.iter().enumerate() {
b[6 + i * 2..8 + i * 2].copy_from_slice(&w.to_ne_bytes());
}
b
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct SinkHdr {
pub eotfs: u8,
pub colorimetry: u8,
pub max_luminance_cv: Option<u8>,
}
impl SinkHdr {
pub fn parse(edid: &[u8]) -> SinkHdr {
let mut sink = SinkHdr::default();
for block in edid.chunks_exact(128).skip(1).filter(|b| b[0] == 0x02) {
let end = (block[2] as usize).clamp(4, 127);
let mut i = 4;
while i < end {
let (tag, len) = (block[i] >> 5, (block[i] & 0x1f) as usize);
let data = &block[i + 1..(i + 1 + len).min(128)];
i += 1 + len;
match (tag, data) {
(7, [5, bits, ..]) => sink.colorimetry = *bits,
(7, [6, eotfs, rest @ ..]) => {
sink.eotfs = *eotfs;
sink.max_luminance_cv = rest.get(1).copied();
}
_ => {}
}
}
}
sink
}
pub fn supports(&self, eotf: u8) -> bool {
self.eotfs & (1 << eotf) != 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn blob_layout() {
let b = Hdr { eotf: EOTF_PQ, max_luma: 1000, min_luma: 50, max_cll: 800, max_fall: 400 }.blob();
assert_eq!(&b[..6], &[0, 0, 0, 0, 2, 0]);
let word = |o: usize| u16::from_ne_bytes([b[o], b[o + 1]]);
assert_eq!((word(6), word(18), word(20)), (8500, 15635, 16450));
assert_eq!((word(22), word(24), word(26), word(28)), (1000, 50, 800, 400));
assert_eq!(&b[30..], &[0, 0]);
}
#[test]
fn sink_blocks() {
let mut edid = vec![0u8; 256];
edid[128] = 0x02;
let blocks = [0xe3, 5, 0xc0, 0, 0xe4, 6, 0x0d, 1, 96];
edid[132..132 + blocks.len()].copy_from_slice(&blocks);
edid[130] = 4 + blocks.len() as u8;
let sink = SinkHdr::parse(&edid);
assert_eq!(sink, SinkHdr { eotfs: 0x0d, colorimetry: 0xc0, max_luminance_cv: Some(96) });
assert!(sink.supports(EOTF_PQ) && sink.supports(EOTF_HLG) && !sink.supports(1));
assert_eq!(SinkHdr::parse(&edid[..128]), SinkHdr::default());
}
}