use crate::model::capabilities::DisplayCapabilities;
use crate::model::color::{
AnalogColorType, Chromaticity, ColorBitDepth, DigitalColorEncoding, DisplayGamma,
};
use crate::model::edid::EdidVersion;
use crate::model::features::DisplayFeatureFlags;
use crate::model::input::{AnalogSyncLevel, VideoInputFlags, VideoInterface};
use crate::model::manufacture::{ManufactureDate, ManufacturerId};
use crate::model::screen::ScreenSize;
pub(crate) fn decode_manufacture_date(week: u8, year: u8) -> ManufactureDate {
let y = year as u16 + 1990;
match week {
0xFF => ManufactureDate::ModelYear(y),
0x00 => ManufactureDate::Manufactured {
week: None,
year: y,
},
w => ManufactureDate::Manufactured {
week: Some(w),
year: y,
},
}
}
fn decode_chromaticity(base: &[u8; 128]) -> Chromaticity {
let lsb0 = base[0x19];
let lsb1 = base[0x1A];
Chromaticity {
red: crate::model::color::ChromaticityPoint {
x_raw: ((base[0x1B] as u16) << 2) | ((lsb0 >> 6) & 0x03) as u16,
y_raw: ((base[0x1C] as u16) << 2) | ((lsb0 >> 4) & 0x03) as u16,
},
green: crate::model::color::ChromaticityPoint {
x_raw: ((base[0x1D] as u16) << 2) | ((lsb0 >> 2) & 0x03) as u16,
y_raw: ((base[0x1E] as u16) << 2) | (lsb0 & 0x03) as u16,
},
blue: crate::model::color::ChromaticityPoint {
x_raw: ((base[0x1F] as u16) << 2) | ((lsb1 >> 6) & 0x03) as u16,
y_raw: ((base[0x20] as u16) << 2) | ((lsb1 >> 4) & 0x03) as u16,
},
white: crate::model::color::ChromaticityPoint {
x_raw: ((base[0x21] as u16) << 2) | ((lsb1 >> 2) & 0x03) as u16,
y_raw: ((base[0x22] as u16) << 2) | (lsb1 & 0x03) as u16,
},
}
}
fn decode_digital_color_encoding(bits: u8) -> DigitalColorEncoding {
match (bits >> 3) & 0x03 {
0b00 => DigitalColorEncoding::Rgb444,
0b01 => DigitalColorEncoding::Rgb444YCbCr444,
0b10 => DigitalColorEncoding::Rgb444YCbCr422,
_ => DigitalColorEncoding::Rgb444YCbCr444YCbCr422,
}
}
fn decode_analog_color_type(bits: u8) -> Option<AnalogColorType> {
match (bits >> 3) & 0x03 {
0b00 => Some(AnalogColorType::Monochrome),
0b01 => Some(AnalogColorType::Rgb),
0b10 => Some(AnalogColorType::NonRgb),
_ => None,
}
}
pub(crate) fn decode_color_bit_depth(bits: u8) -> Option<ColorBitDepth> {
match bits & 0x07 {
0b001 => Some(ColorBitDepth::Depth6),
0b010 => Some(ColorBitDepth::Depth8),
0b011 => Some(ColorBitDepth::Depth10),
0b100 => Some(ColorBitDepth::Depth12),
0b101 => Some(ColorBitDepth::Depth14),
0b110 => Some(ColorBitDepth::Depth16),
_ => None,
}
}
fn decode_video_interface(bits: u8) -> Option<VideoInterface> {
match bits & 0x0F {
0x1 => Some(VideoInterface::Dvi),
0x2 => Some(VideoInterface::HdmiA),
0x3 => Some(VideoInterface::HdmiB),
0x4 => Some(VideoInterface::Mddi),
0x5 => Some(VideoInterface::DisplayPort),
_ => None,
}
}
fn decode_analog_sync_level(bits: u8) -> AnalogSyncLevel {
match (bits >> 5) & 0x03 {
0b00 => AnalogSyncLevel::V700_300,
0b01 => AnalogSyncLevel::V714_286,
0b10 => AnalogSyncLevel::V1000_400,
_ => AnalogSyncLevel::V700_0,
}
}
fn decode_screen_size(byte15: u8, byte16: u8) -> Option<ScreenSize> {
match (byte15, byte16) {
(0, 0) => None,
(w, h) if w != 0 && h != 0 => Some(ScreenSize::Physical {
width_cm: w,
height_cm: h,
}),
(v, 0) => Some(ScreenSize::Landscape(v)),
(0, v) => Some(ScreenSize::Portrait(v)),
_ => unreachable!(),
}
}
pub(super) fn decode_header_fields(base: &[u8; 128], caps: &mut DisplayCapabilities) -> bool {
let id_raw = ((base[0x08] as u16) << 8) | (base[0x09] as u16);
let char1 = ((id_raw >> 10) & 0x1F) as u8;
let char2 = ((id_raw >> 5) & 0x1F) as u8;
let char3 = (id_raw & 0x1F) as u8;
let manufacturer_valid =
(1..=26).contains(&char1) && (1..=26).contains(&char2) && (1..=26).contains(&char3);
if manufacturer_valid {
caps.manufacturer = Some(ManufacturerId([
char1 + b'A' - 1,
char2 + b'A' - 1,
char3 + b'A' - 1,
]));
}
let product_code = ((base[0x0B] as u16) << 8) | (base[0x0A] as u16);
if product_code != 0 {
caps.product_code = Some(product_code);
}
let serial = ((base[0x0F] as u32) << 24)
| ((base[0x0E] as u32) << 16)
| ((base[0x0D] as u32) << 8)
| (base[0x0C] as u32);
if serial != 0 {
caps.serial_number = Some(serial);
}
caps.manufacture_date = Some(decode_manufacture_date(base[16], base[17]));
caps.edid_version = Some(EdidVersion {
version: base[18],
revision: base[19],
});
caps.chromaticity = decode_chromaticity(base);
caps.gamma = DisplayGamma::from_edid_byte(base[0x17]);
caps.display_features = Some(DisplayFeatureFlags::from_bits_truncate(base[0x18]));
let is_digital = base[0x14] & 0x80 != 0;
let edid_revision = base[19];
if is_digital && edid_revision >= 4 {
caps.digital_color_encoding = Some(decode_digital_color_encoding(base[0x18]));
} else if !is_digital {
caps.analog_color_type = decode_analog_color_type(base[0x18]);
}
let video_input = VideoInputFlags::from_bits_truncate(base[0x14]);
caps.digital = video_input.contains(VideoInputFlags::DIGITAL);
if caps.digital {
caps.color_bit_depth = decode_color_bit_depth(base[0x14] >> 4);
caps.video_interface = decode_video_interface(base[0x14]);
} else {
caps.analog_sync_level = Some(decode_analog_sync_level(base[0x14]));
}
caps.screen_size = decode_screen_size(base[0x15], base[0x16]);
manufacturer_valid
}
#[cfg(test)]
#[cfg(any(feature = "alloc", feature = "std"))]
mod tests {
use super::decode_chromaticity;
use crate::capabilities::base::BaseBlockHandler;
use crate::model::capabilities::DisplayCapabilities;
use crate::model::color::{AnalogColorType, ColorBitDepth, DigitalColorEncoding, DisplayGamma};
use crate::model::diagnostics::EdidWarning;
use crate::model::edid::EdidVersion;
use crate::model::extension::ExtensionHandler;
use crate::model::features::DisplayFeatureFlags;
use crate::model::input::{AnalogSyncLevel, VideoInterface};
use crate::model::manufacture::{ManufactureDate, ManufacturerId};
use crate::model::prelude::Vec;
use crate::model::screen::ScreenSize;
#[test]
fn test_gamma() {
let mut base = [0u8; 128];
base[0x17] = 120; let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.gamma, Some(DisplayGamma::from_edid_byte(120).unwrap()));
assert!((caps.gamma.unwrap().value() - 2.20).abs() < 0.001);
base[0x17] = 0xFF; let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.gamma, None);
}
#[test]
fn test_edid_version() {
let mut base = [0u8; 128];
base[18] = 1;
base[19] = 4;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(
caps.edid_version,
Some(EdidVersion {
version: 1,
revision: 4
})
);
}
#[test]
fn test_manufacture_date() {
let mut base = [0u8; 128];
base[16] = 12;
base[17] = 30; let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(
caps.manufacture_date,
Some(ManufactureDate::Manufactured {
week: Some(12),
year: 2020
})
);
base[16] = 0x00;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(
caps.manufacture_date,
Some(ManufactureDate::Manufactured {
week: None,
year: 2020
})
);
base[16] = 0xFF;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(
caps.manufacture_date,
Some(ManufactureDate::ModelYear(2020))
);
}
#[test]
fn test_screen_size() {
let mut base = [0u8; 128];
base[0x15] = 60;
base[0x16] = 34;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(
caps.screen_size,
Some(ScreenSize::Physical {
width_cm: 60,
height_cm: 34
})
);
base[0x15] = 196;
base[0x16] = 0;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.screen_size, Some(ScreenSize::Landscape(196)));
let ratio = caps.screen_size.unwrap().landscape_ratio().unwrap();
assert!((ratio - 2.95).abs() < 0.001);
base[0x15] = 0;
base[0x16] = 101;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.screen_size, Some(ScreenSize::Portrait(101)));
let ratio = caps.screen_size.unwrap().portrait_ratio().unwrap();
assert!((ratio - 0.5).abs() < 0.001);
base[0x15] = 0;
base[0x16] = 0;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.screen_size, None);
}
#[test]
fn test_analog_sync_level() {
let mut base = [0u8; 128];
base[0x14] = 0x00;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.analog_sync_level, Some(AnalogSyncLevel::V700_300));
assert_eq!(caps.color_bit_depth, None);
assert_eq!(caps.video_interface, None);
base[0x14] = 0x20;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.analog_sync_level, Some(AnalogSyncLevel::V714_286));
base[0x14] = 0x40;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.analog_sync_level, Some(AnalogSyncLevel::V1000_400));
base[0x14] = 0x60;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.analog_sync_level, Some(AnalogSyncLevel::V700_0));
base[0x14] = 0x80;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.analog_sync_level, None);
}
#[test]
fn test_color_bit_depth_and_video_interface() {
let mut base = [0u8; 128];
base[0x14] = 0x80 | 0x25;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert!(caps.digital);
assert_eq!(caps.color_bit_depth, Some(ColorBitDepth::Depth8));
assert_eq!(caps.video_interface, Some(VideoInterface::DisplayPort));
base[0x14] = 0x80;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.color_bit_depth, None);
assert_eq!(caps.video_interface, None);
base[0x14] = 0x00;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert!(!caps.digital);
assert_eq!(caps.color_bit_depth, None);
assert_eq!(caps.video_interface, None);
}
#[test]
fn test_chromaticity() {
let mut base = [0u8; 128];
base[0x1B] = (655u16 >> 2) as u8;
base[0x1C] = (338u16 >> 2) as u8;
base[0x1D] = (307u16 >> 2) as u8;
base[0x1E] = (614u16 >> 2) as u8;
base[0x1F] = (154u16 >> 2) as u8;
base[0x20] = (61u16 >> 2) as u8;
base[0x21] = (320u16 >> 2) as u8;
base[0x22] = (337u16 >> 2) as u8;
base[0x19] =
(((655u16 & 3) << 6) | ((338u16 & 3) << 4) | ((307u16 & 3) << 2) | (614u16 & 3)) as u8;
base[0x1A] = (((154u16 & 3) << 6) | ((61u16 & 3) << 4) | (337u16 & 3)) as u8;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.chromaticity, decode_chromaticity(&base));
assert_eq!(caps.chromaticity.red.x_raw, 655);
assert_eq!(caps.chromaticity.red.y_raw, 338);
assert_eq!(caps.chromaticity.green.x_raw, 307);
assert_eq!(caps.chromaticity.white.x_raw, 320);
assert!((caps.chromaticity.red.x() - 0.640).abs() < 0.002);
assert!((caps.chromaticity.white.x() - 0.3125).abs() < 0.002);
}
#[test]
fn test_color_type() {
let mut base = [0u8; 128];
base[0x14] = 0x80; base[19] = 4; base[0x18] = 0x18; let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(
caps.digital_color_encoding,
Some(DigitalColorEncoding::Rgb444YCbCr444YCbCr422)
);
assert_eq!(caps.analog_color_type, None);
base[19] = 3;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.digital_color_encoding, None);
assert_eq!(caps.analog_color_type, None);
base[0x14] = 0x00; base[0x18] = 0x08; let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.digital_color_encoding, None);
assert_eq!(caps.analog_color_type, Some(AnalogColorType::Rgb));
base[0x18] = 0x18;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.analog_color_type, None);
}
#[test]
fn test_display_features() {
let mut base = [0u8; 128];
base[0x18] = 0xE2;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
let flags = caps.display_features.unwrap();
assert!(flags.contains(DisplayFeatureFlags::DPMS_STANDBY));
assert!(flags.contains(DisplayFeatureFlags::DPMS_SUSPEND));
assert!(flags.contains(DisplayFeatureFlags::DPMS_ACTIVE_OFF));
assert!(flags.contains(DisplayFeatureFlags::PREFERRED_TIMING));
assert!(!flags.contains(DisplayFeatureFlags::SRGB));
assert!(!flags.contains(DisplayFeatureFlags::CONTINUOUS_TIMINGS));
base[0x18] = 0x07;
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
let flags = caps.display_features.unwrap();
assert!(flags.contains(DisplayFeatureFlags::SRGB));
assert!(flags.contains(DisplayFeatureFlags::PREFERRED_TIMING));
assert!(flags.contains(DisplayFeatureFlags::CONTINUOUS_TIMINGS));
}
#[test]
fn test_identification() {
let mut base = [0u8; 128];
base[0x08] = 0x4C;
base[0x09] = 0x2D;
base[0x0A] = 0x34;
base[0x0B] = 0x12;
base[0x0C] = 0x78;
base[0x0D] = 0x56;
base[0x0E] = 0x34;
base[0x0F] = 0x12;
base[0x14] = 0x80;
base[0x15] = 51;
base[0x16] = 29;
base[0x36..0x3B].copy_from_slice(&[0x00, 0x00, 0x00, 0xFC, 0x00]);
base[0x3B..0x3F].copy_from_slice(b"PIAF");
base[0x3F] = 0x0A;
base[0x40..0x48].fill(0x20);
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut Vec::new());
assert_eq!(caps.manufacturer, Some(ManufacturerId([b'S', b'A', b'M'])));
assert_eq!(caps.product_code, Some(0x1234));
assert_eq!(caps.serial_number, Some(0x12345678));
assert!(caps.digital);
assert_eq!(
caps.screen_size,
Some(ScreenSize::Physical {
width_cm: 51,
height_cm: 29
})
);
assert_eq!(caps.display_name.as_deref(), Some("PIAF"));
}
#[test]
fn test_invalid_manufacturer_id_zero() {
let mut base = [0u8; 128];
base[0x08] = 0x00;
base[0x09] = 0x00;
let mut warnings = Vec::new();
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut warnings);
assert_eq!(caps.manufacturer, None);
assert!(
warnings.iter().any(|w| (**w).downcast_ref::<EdidWarning>()
== Some(&EdidWarning::InvalidManufacturerId))
);
}
#[test]
fn test_invalid_manufacturer_id_out_of_range() {
let mut base = [0u8; 128];
base[0x08] = 0x6C;
base[0x09] = 0x21;
let mut warnings = Vec::new();
let mut caps = DisplayCapabilities::default();
BaseBlockHandler.process(&[&base], &mut caps, &mut warnings);
assert_eq!(caps.manufacturer, None);
assert!(
warnings.iter().any(|w| (**w).downcast_ref::<EdidWarning>()
== Some(&EdidWarning::InvalidManufacturerId))
);
}
}