use num::FromPrimitive;
use num_derive::{FromPrimitive, ToPrimitive};
use serde::Serialize;
use zerocopy::{BigEndian, Immutable, IntoBytes, KnownLayout, TryFromBytes, U16, U32};
use crate::{tag::tagdata::ChromaticityData, tag::tagdata::DataSignature};
use colorimetry::xyz as cmt;
const ITU: [cmt::Chromaticity; 3] = [
cmt::Chromaticity::new(0.640, 0.330),
cmt::Chromaticity::new(0.300, 0.600),
cmt::Chromaticity::new(0.150, 0.060),
];
const SMPTE: [cmt::Chromaticity; 3] = [
cmt::Chromaticity::new(0.630, 0.340),
cmt::Chromaticity::new(0.310, 0.595),
cmt::Chromaticity::new(0.155, 0.070),
];
const EBU: [cmt::Chromaticity; 3] = [
cmt::Chromaticity::new(0.640, 0.330),
cmt::Chromaticity::new(0.290, 0.600),
cmt::Chromaticity::new(0.150, 0.060),
];
const P22: [cmt::Chromaticity; 3] = [
cmt::Chromaticity::new(0.625, 0.340),
cmt::Chromaticity::new(0.280, 0.605),
cmt::Chromaticity::new(0.155, 0.070),
];
const P3: [cmt::Chromaticity; 3] = [
cmt::Chromaticity::new(0.680, 0.320),
cmt::Chromaticity::new(0.265, 0.690),
cmt::Chromaticity::new(0.150, 0.060),
];
const ITU2020: [cmt::Chromaticity; 3] = [
cmt::Chromaticity::new(0.708, 0.292),
cmt::Chromaticity::new(0.170, 0.797),
cmt::Chromaticity::new(0.131, 0.046),
];
#[derive(
Debug,
Default,
Serialize,
FromPrimitive,
ToPrimitive,
Clone,
Copy,
PartialEq,
TryFromBytes,
IntoBytes,
KnownLayout,
Immutable,
strum::Display,
)]
#[repr(C)]
pub enum StandardPrimaries {
#[default]
ITU = 0x0001, SMPTE = 0x0002, EBU = 0x0003, P22 = 0x0004,
P3 = 0x0005, ITU2020 = 0x0006, }
impl StandardPrimaries {
pub fn rgb(&self) -> Option<[cmt::Chromaticity; 3]> {
match self {
StandardPrimaries::ITU => Some(ITU),
StandardPrimaries::SMPTE => Some(SMPTE),
StandardPrimaries::EBU => Some(EBU),
StandardPrimaries::P22 => Some(P22),
StandardPrimaries::P3 => Some(P3),
StandardPrimaries::ITU2020 => Some(ITU2020),
}
}
}
#[derive(TryFromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C, packed)]
struct Layout {
type_signature: [u8; 4],
reserved: [u8; 4],
channels: U16<BigEndian>,
primaries: U16<BigEndian>,
data: [[U32<BigEndian>; 2]],
}
#[derive(TryFromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C, packed)]
struct WriteLayout<const N: usize> {
type_signature: [u8; 4],
reserved: [u8; 4],
channels: U16<BigEndian>,
primaries: U16<BigEndian>,
data: [[U32<BigEndian>; 2]; N],
}
impl WriteLayout<1> {
pub fn new_primary(sp: StandardPrimaries) -> Self {
let mut map = WriteLayout::<1>::default();
map.primaries
.set(num::ToPrimitive::to_u16(&sp).unwrap_or_default());
map
}
pub fn new_custom(chromaticities: [cmt::Chromaticity; 3]) -> Self {
let mut map = WriteLayout::<1>::default();
map.channels.set(3); map.primaries.set(0);
for (i, chromaticity) in chromaticities.iter().enumerate() {
if i < map.data.len() {
map.data[i][0].set((chromaticity.x() * u16::MAX as f64) as u32);
map.data[i][1].set((chromaticity.y() * u16::MAX as f64) as u32);
}
}
map
}
}
impl Default for WriteLayout<1> {
fn default() -> Self {
WriteLayout {
type_signature: DataSignature::ChromaticityData.into(),
reserved: [0; 4],
channels: U16::new(1),
primaries: U16::new(1), data: [[U32::new(0), U32::new(0)]],
}
}
}
#[derive(Serialize)]
pub struct ChromaticityType {
#[serde(skip_serializing_if = "Option::is_none")]
primaries: Option<StandardPrimaries>,
#[serde(skip_serializing_if = "Option::is_none")]
chromaticities: Option<Vec<[f64; 2]>>,
}
impl From<&ChromaticityData> for ChromaticityType {
fn from(chromaticity: &ChromaticityData) -> Self {
let chromaticities_opt = chromaticity.get_custom_chromaticities();
let primaries = FromPrimitive::from_u16(chromaticity.chromaticity_map().primaries.get());
let chromaticities = chromaticities_opt
.map(|chromaticities| chromaticities.iter().map(|c| [c.x(), c.y()]).collect());
ChromaticityType {
primaries,
chromaticities,
}
}
}
impl ChromaticityData {
fn chromaticity_map(&self) -> &Layout {
Layout::try_ref_from_bytes(self.0.as_slice())
.expect("Failed to convert ChromaticityMap from bytes")
}
pub fn get_custom_chromaticities(&self) -> Option<[cmt::Chromaticity; 3]> {
let t = self.chromaticity_map();
if t.channels.get() != 3 {
return None;
}
if t.primaries.get() != 0 {
None
} else {
let values: Vec<[f64; 2]> = t
.data
.iter()
.map(|&x| {
[
x[0].get() as f64 / u16::MAX as f64,
x[1].get() as f64 / u16::MAX as f64,
]
})
.collect();
if values.len() < 3 {
None
} else {
Some([
cmt::Chromaticity::new(values[0][0], values[0][1]),
cmt::Chromaticity::new(values[1][0], values[1][1]),
cmt::Chromaticity::new(values[2][0], values[2][1]),
])
}
}
}
pub fn set_standard(&mut self, primaries: StandardPrimaries) {
let map = WriteLayout::new_primary(primaries);
self.0 = map.as_bytes().to_vec();
}
pub fn set_custom(&mut self, chromaticities: [cmt::Chromaticity; 3]) {
let map = WriteLayout::new_custom(chromaticities);
self.0 = map.as_bytes().to_vec();
}
}