use { crate::{ cmm::{ cmm_description::{ ColorDescription, ColorDescriptionIds, LinearColorDescription, LinearColorDescriptionId, LinearColorDescriptionIds, }, cmm_eotf::Eotf, cmm_luminance::{Luminance, TargetLuminance}, cmm_primaries::{NamedPrimaries, Primaries}, }, utils::{copyhashmap::CopyHashMap, numcell::NumCell, ordered_float::F64}, }, std::rc::{Rc, Weak}, }; pub struct ColorManager { linear_ids: LinearColorDescriptionIds, linear_descriptions: CopyHashMap>, complete_descriptions: CopyHashMap>, shared: Rc, srgb_gamma22: Rc, srgb_linear: Rc, windows_scrgb: Rc, } #[derive(Debug, Default)] pub(super) struct Shared { pub(super) dead_linear: NumCell, pub(super) dead_complete: NumCell, pub(super) complete_ids: ColorDescriptionIds, } #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] struct LinearDescriptionKey { primaries: Primaries, luminance: Luminance, target_primaries: Primaries, target_luminance: TargetLuminance, max_cll: Option, max_fall: Option, } #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] struct CompleteDescriptionKey { linear: LinearColorDescriptionId, named_primaries: Option, eotf: Eotf, } impl ColorManager { pub fn new() -> Rc { let linear_ids = LinearColorDescriptionIds::default(); let linear_descriptions = CopyHashMap::default(); let complete_descriptions = CopyHashMap::default(); let shared = Rc::new(Shared::default()); let _ = shared.complete_ids.next(); let srgb_gamma22 = get_description( &shared, &linear_descriptions, &complete_descriptions, &linear_ids, Some(NamedPrimaries::Srgb), Primaries::SRGB, Luminance::SRGB, Eotf::Gamma22, Primaries::SRGB, Luminance::SRGB.to_target(), None, None, ); let srgb_linear = get_description2( &shared, &srgb_gamma22.linear, &complete_descriptions, Some(NamedPrimaries::Srgb), Eotf::Linear, ); let windows_scrgb = get_description( &shared, &linear_descriptions, &complete_descriptions, &linear_ids, Some(NamedPrimaries::Srgb), Primaries::SRGB, Luminance::WINDOWS_SCRGB, Eotf::Linear, Primaries::BT2020, Luminance::ST2084_PQ.to_target(), None, None, ); Rc::new(Self { linear_ids, linear_descriptions, complete_descriptions, shared, srgb_gamma22, srgb_linear, windows_scrgb, }) } pub fn srgb_gamma22(&self) -> &Rc { &self.srgb_gamma22 } pub fn srgb_linear(&self) -> &Rc { &self.srgb_linear } pub fn windows_scrgb(&self) -> &Rc { &self.windows_scrgb } pub fn get_description( self: &Rc, named_primaries: Option, primaries: Primaries, luminance: Luminance, eotf: Eotf, target_primaries: Primaries, target_luminance: TargetLuminance, max_cll: Option, max_fall: Option, ) -> Rc { get_description( &self.shared, &self.linear_descriptions, &self.complete_descriptions, &self.linear_ids, named_primaries, primaries, luminance, eotf, target_primaries, target_luminance, max_cll, max_fall, ) } pub fn get_with_tf( self: &Rc, cd: &Rc, eotf: Eotf, ) -> Rc { get_description2( &self.shared, &cd.linear, &self.complete_descriptions, cd.named_primaries, eotf, ) } } fn get_description( shared: &Rc, linear_descriptions: &CopyHashMap>, complete_descriptions: &CopyHashMap>, linear_ids: &LinearColorDescriptionIds, named_primaries: Option, primaries: Primaries, luminance: Luminance, eotf: Eotf, target_primaries: Primaries, target_luminance: TargetLuminance, max_cll: Option, max_fall: Option, ) -> Rc { macro_rules! gc { ($d:ident, $i:expr) => { if $d.len() > 16 && $i.get() * 2 > $d.len() { $d.lock().retain(|_, d| d.strong_count() > 0); $i.set(0); } }; } gc!(linear_descriptions, &shared.dead_linear); gc!(complete_descriptions, &shared.dead_complete); let key = LinearDescriptionKey { primaries, luminance, target_primaries, target_luminance, max_cll, max_fall, }; if let Some(d) = linear_descriptions.get(&key) { if let Some(d) = d.upgrade() { return get_description2(shared, &d, complete_descriptions, named_primaries, eotf); } shared.dead_linear.fetch_sub(1); } let (xyz_from_local, local_from_xyz) = primaries.matrices(); let d = Rc::new(LinearColorDescription { id: linear_ids.next(), primaries, xyz_from_local, local_from_xyz, luminance, target_primaries, target_luminance, max_cll, max_fall, shared: shared.clone(), }); linear_descriptions.set(key, Rc::downgrade(&d)); let key = CompleteDescriptionKey { linear: d.id, named_primaries, eotf, }; let d = Rc::new(ColorDescription { id: shared.complete_ids.next(), linear: d, named_primaries, eotf, shared: shared.clone(), }); complete_descriptions.set(key, Rc::downgrade(&d)); d } fn get_description2( shared: &Rc, ld: &Rc, complete_descriptions: &CopyHashMap>, named_primaries: Option, eotf: Eotf, ) -> Rc { let key = CompleteDescriptionKey { linear: ld.id, named_primaries, eotf, }; if let Some(d) = complete_descriptions.get(&key) { if let Some(d) = d.upgrade() { return d; } shared.dead_complete.fetch_sub(1); } let d = Rc::new(ColorDescription { id: shared.complete_ids.next(), linear: ld.clone(), named_primaries, eotf, shared: shared.clone(), }); complete_descriptions.set(key, Rc::downgrade(&d)); d }