use alloc::string::String;
use alloc::vec::Vec;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum AuxiliaryImageType {
Alpha,
Depth,
PortraitMatte,
HdrGainMap,
SkinMatte,
TeethMatte,
HairMatte,
GlassesMatte,
Other(String),
}
impl AuxiliaryImageType {
#[must_use]
pub fn from_urn(urn: &str) -> Self {
match urn {
"urn:mpeg:hevc:2015:auxid:1" | "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha" => {
Self::Alpha
}
"urn:mpeg:hevc:2015:auxid:2" | "urn:mpeg:mpegB:cicp:systems:auxiliary:depth" => {
Self::Depth
}
"urn:com:apple:photo:2018:aux:portraiteffectsmatte" => Self::PortraitMatte,
"urn:com:apple:photo:2020:aux:hdrgainmap" => Self::HdrGainMap,
"urn:com:apple:photo:2019:aux:semanticskinmatte" => Self::SkinMatte,
"urn:com:apple:photo:2019:aux:semanticteethmatte" => Self::TeethMatte,
"urn:com:apple:photo:2019:aux:semantichairmatte" => Self::HairMatte,
"urn:com:apple:photo:2020:aux:semanticglassesmatte" => Self::GlassesMatte,
other => Self::Other(String::from(other)),
}
}
#[must_use]
pub fn urn(&self) -> &str {
match self {
Self::Alpha => "urn:mpeg:hevc:2015:auxid:1",
Self::Depth => "urn:mpeg:hevc:2015:auxid:2",
Self::PortraitMatte => "urn:com:apple:photo:2018:aux:portraiteffectsmatte",
Self::HdrGainMap => "urn:com:apple:photo:2020:aux:hdrgainmap",
Self::SkinMatte => "urn:com:apple:photo:2019:aux:semanticskinmatte",
Self::TeethMatte => "urn:com:apple:photo:2019:aux:semanticteethmatte",
Self::HairMatte => "urn:com:apple:photo:2019:aux:semantichairmatte",
Self::GlassesMatte => "urn:com:apple:photo:2020:aux:semanticglassesmatte",
Self::Other(s) => s.as_str(),
}
}
}
impl core::fmt::Display for AuxiliaryImageType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Alpha => write!(f, "Alpha"),
Self::Depth => write!(f, "Depth"),
Self::PortraitMatte => write!(f, "PortraitMatte"),
Self::HdrGainMap => write!(f, "HdrGainMap"),
Self::SkinMatte => write!(f, "SkinMatte"),
Self::TeethMatte => write!(f, "TeethMatte"),
Self::HairMatte => write!(f, "HairMatte"),
Self::GlassesMatte => write!(f, "GlassesMatte"),
Self::Other(s) => write!(f, "Other({s})"),
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[non_exhaustive]
pub enum DepthRepresentationType {
#[default]
UniformInverseZ,
UniformDisparity,
UniformZ,
NonuniformDisparity,
}
impl DepthRepresentationType {
#[must_use]
pub fn from_code(code: u8) -> Option<Self> {
match code {
0 => Some(Self::UniformInverseZ),
1 => Some(Self::UniformDisparity),
2 => Some(Self::UniformZ),
3 => Some(Self::NonuniformDisparity),
_ => None,
}
}
#[must_use]
pub fn code(self) -> u8 {
match self {
Self::UniformInverseZ => 0,
Self::UniformDisparity => 1,
Self::UniformZ => 2,
Self::NonuniformDisparity => 3,
}
}
}
impl core::fmt::Display for DepthRepresentationType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::UniformInverseZ => write!(f, "UniformInverseZ"),
Self::UniformDisparity => write!(f, "UniformDisparity"),
Self::UniformZ => write!(f, "UniformZ"),
Self::NonuniformDisparity => write!(f, "NonuniformDisparity"),
}
}
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct DepthRepresentationInfo {
pub z_near: Option<f64>,
pub z_far: Option<f64>,
pub d_min: Option<f64>,
pub d_max: Option<f64>,
pub representation_type: DepthRepresentationType,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct DepthMap {
pub data: Vec<u16>,
pub width: u32,
pub height: u32,
pub bit_depth: u8,
pub depth_info: DepthRepresentationInfo,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct AuxiliaryImageDescriptor {
pub aux_type: AuxiliaryImageType,
pub item_id: u32,
pub dimensions: Option<(u32, u32)>,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct SegmentationMatte {
pub data: alloc::vec::Vec<u8>,
pub width: u32,
pub height: u32,
pub matte_type: AuxiliaryImageType,
}
pub(crate) fn parse_depth_representation_info(subtype_data: &[u8]) -> DepthRepresentationInfo {
if subtype_data.len() < 2 {
return DepthRepresentationInfo::default();
}
let rep_type_code = subtype_data[0];
let representation_type = DepthRepresentationType::from_code(rep_type_code)
.unwrap_or(DepthRepresentationType::UniformInverseZ);
let flags = subtype_data[1];
let z_near_flag = (flags & 0x01) != 0;
let z_far_flag = (flags & 0x02) != 0;
let d_min_flag = (flags & 0x04) != 0;
let d_max_flag = (flags & 0x08) != 0;
let mut pos = 2;
let z_near = if z_near_flag {
parse_depth_rep_value(subtype_data, &mut pos)
} else {
None
};
let z_far = if z_far_flag {
parse_depth_rep_value(subtype_data, &mut pos)
} else {
None
};
let d_min = if d_min_flag {
parse_depth_rep_value(subtype_data, &mut pos)
} else {
None
};
let d_max = if d_max_flag {
parse_depth_rep_value(subtype_data, &mut pos)
} else {
None
};
DepthRepresentationInfo {
z_near,
z_far,
d_min,
d_max,
representation_type,
}
}
fn parse_depth_rep_value(data: &[u8], pos: &mut usize) -> Option<f64> {
if *pos + 4 > data.len() {
return None;
}
let mantissa = i16::from_be_bytes([data[*pos], data[*pos + 1]]);
let exponent = data[*pos + 2];
let sign = data[*pos + 3];
*pos += 4;
let value = (mantissa as f64) * pow2_f64(exponent as i32);
if sign != 0 { Some(-value) } else { Some(value) }
}
fn pow2_f64(exp: i32) -> f64 {
if exp == 0 {
1.0
} else if exp > 0 && exp < 64 {
(1u64 << exp as u32) as f64
} else {
let mut result = 1.0f64;
if exp > 0 {
for _ in 0..exp {
result *= 2.0;
}
} else {
for _ in 0..(-exp) {
result *= 0.5;
}
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_auxiliary_type_from_urn_alpha() {
assert_eq!(
AuxiliaryImageType::from_urn("urn:mpeg:hevc:2015:auxid:1"),
AuxiliaryImageType::Alpha
);
assert_eq!(
AuxiliaryImageType::from_urn("urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"),
AuxiliaryImageType::Alpha
);
}
#[test]
fn test_auxiliary_type_from_urn_depth() {
assert_eq!(
AuxiliaryImageType::from_urn("urn:mpeg:hevc:2015:auxid:2"),
AuxiliaryImageType::Depth
);
assert_eq!(
AuxiliaryImageType::from_urn("urn:mpeg:mpegB:cicp:systems:auxiliary:depth"),
AuxiliaryImageType::Depth
);
}
#[test]
fn test_auxiliary_type_from_urn_apple_types() {
assert_eq!(
AuxiliaryImageType::from_urn("urn:com:apple:photo:2018:aux:portraiteffectsmatte"),
AuxiliaryImageType::PortraitMatte
);
assert_eq!(
AuxiliaryImageType::from_urn("urn:com:apple:photo:2020:aux:hdrgainmap"),
AuxiliaryImageType::HdrGainMap
);
assert_eq!(
AuxiliaryImageType::from_urn("urn:com:apple:photo:2019:aux:semanticskinmatte"),
AuxiliaryImageType::SkinMatte
);
assert_eq!(
AuxiliaryImageType::from_urn("urn:com:apple:photo:2019:aux:semanticteethmatte"),
AuxiliaryImageType::TeethMatte
);
assert_eq!(
AuxiliaryImageType::from_urn("urn:com:apple:photo:2019:aux:semantichairmatte"),
AuxiliaryImageType::HairMatte
);
assert_eq!(
AuxiliaryImageType::from_urn("urn:com:apple:photo:2020:aux:semanticglassesmatte"),
AuxiliaryImageType::GlassesMatte
);
}
#[test]
fn test_auxiliary_type_from_urn_unknown() {
let t = AuxiliaryImageType::from_urn("urn:example:custom:type");
assert_eq!(
t,
AuxiliaryImageType::Other(String::from("urn:example:custom:type"))
);
}
#[test]
fn test_auxiliary_type_from_urn_empty() {
let t = AuxiliaryImageType::from_urn("");
assert_eq!(t, AuxiliaryImageType::Other(String::from("")));
}
#[test]
fn test_auxiliary_type_roundtrip() {
let types = [
AuxiliaryImageType::Alpha,
AuxiliaryImageType::Depth,
AuxiliaryImageType::PortraitMatte,
AuxiliaryImageType::HdrGainMap,
AuxiliaryImageType::SkinMatte,
AuxiliaryImageType::TeethMatte,
AuxiliaryImageType::HairMatte,
AuxiliaryImageType::GlassesMatte,
];
for t in &types {
let urn = t.urn();
let parsed = AuxiliaryImageType::from_urn(urn);
assert_eq!(&parsed, t, "roundtrip failed for {t}");
}
}
#[test]
fn test_depth_representation_type_from_code() {
assert_eq!(
DepthRepresentationType::from_code(0),
Some(DepthRepresentationType::UniformInverseZ)
);
assert_eq!(
DepthRepresentationType::from_code(1),
Some(DepthRepresentationType::UniformDisparity)
);
assert_eq!(
DepthRepresentationType::from_code(2),
Some(DepthRepresentationType::UniformZ)
);
assert_eq!(
DepthRepresentationType::from_code(3),
Some(DepthRepresentationType::NonuniformDisparity)
);
assert_eq!(DepthRepresentationType::from_code(4), None);
assert_eq!(DepthRepresentationType::from_code(255), None);
}
#[test]
fn test_depth_representation_type_roundtrip() {
for code in 0..4 {
let t = DepthRepresentationType::from_code(code).unwrap();
assert_eq!(t.code(), code);
}
}
#[test]
fn test_parse_depth_representation_info_empty() {
let info = parse_depth_representation_info(&[]);
assert_eq!(
info.representation_type,
DepthRepresentationType::UniformInverseZ
);
assert!(info.z_near.is_none());
assert!(info.z_far.is_none());
assert!(info.d_min.is_none());
assert!(info.d_max.is_none());
}
#[test]
fn test_parse_depth_representation_info_type_only() {
let info = parse_depth_representation_info(&[2, 0]);
assert_eq!(info.representation_type, DepthRepresentationType::UniformZ);
assert!(info.z_near.is_none());
}
#[test]
fn test_parse_depth_representation_info_with_z_near_far() {
let data = [
0, 0x03, 0x00, 0x64, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, ];
let info = parse_depth_representation_info(&data);
assert_eq!(
info.representation_type,
DepthRepresentationType::UniformInverseZ
);
assert!((info.z_near.unwrap() - 100.0).abs() < f64::EPSILON);
assert!((info.z_far.unwrap() - 500.0).abs() < f64::EPSILON);
assert!(info.d_min.is_none());
assert!(info.d_max.is_none());
}
#[test]
fn test_parse_depth_representation_info_with_all_values() {
let data = [
1, 0x0F, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x32, 0x02, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0xFF, 0x00, 0x00, ];
let info = parse_depth_representation_info(&data);
assert_eq!(
info.representation_type,
DepthRepresentationType::UniformDisparity
);
assert!((info.z_near.unwrap() - 20.0).abs() < f64::EPSILON);
assert!((info.z_far.unwrap() - 200.0).abs() < f64::EPSILON);
assert!((info.d_min.unwrap() - (-1.0)).abs() < f64::EPSILON);
assert!((info.d_max.unwrap() - 255.0).abs() < f64::EPSILON);
}
#[test]
fn test_parse_depth_representation_info_truncated() {
let data = [0, 0x01, 0x00]; let info = parse_depth_representation_info(&data);
assert!(info.z_near.is_none());
}
#[test]
fn test_pow2_f64() {
assert!((pow2_f64(0) - 1.0).abs() < f64::EPSILON);
assert!((pow2_f64(1) - 2.0).abs() < f64::EPSILON);
assert!((pow2_f64(10) - 1024.0).abs() < f64::EPSILON);
}
#[test]
fn test_display_auxiliary_type() {
assert_eq!(format!("{}", AuxiliaryImageType::Depth), "Depth");
assert_eq!(
format!("{}", AuxiliaryImageType::Other(String::from("custom"))),
"Other(custom)"
);
}
#[test]
fn test_display_depth_representation_type() {
assert_eq!(
format!("{}", DepthRepresentationType::UniformInverseZ),
"UniformInverseZ"
);
assert_eq!(
format!("{}", DepthRepresentationType::NonuniformDisparity),
"NonuniformDisparity"
);
}
}