jay-compositor 1.15.0

The Jay compositor
use crate::utils::bitfield::Bitfield;
use crate::utils::copyhashmap;
use crate::utils::copyhashmap::CopyHashMap;
use crate::utils::fx_hash::FxBuildHasher;
use crate::utils::refcounted::RefCounted;
use jay_proc::jay_clone;
use jay_proc::jay_hash;
use std::cell::RefCell;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;

pub const SCALE_BASE: u32 = 120;
const BASE64: i64 = SCALE_BASE as i64;
pub const SCALE_BASEF: f64 = SCALE_BASE as f64;

#[jay_hash]
#[derive(Copy, Clone, Eq, Ord, PartialOrd)]
#[repr(transparent)]
pub struct Scale(u32);

impl Default for Scale {
    fn default() -> Self {
        Scale::from_int(1)
    }
}

impl Scale {
    pub const fn from_int(f: u32) -> Self {
        Self(f.saturating_mul(SCALE_BASE))
    }

    pub fn from_f64(f: f64) -> Self {
        Self((f * SCALE_BASEF).round() as u32)
    }

    pub fn from_f64_as_float(f: f64) -> Self {
        Self(((f * (SCALE_BASEF / 15.0)).round() as u32).saturating_mul(15))
    }

    pub fn to_f64(self) -> f64 {
        self.0 as f64 / SCALE_BASEF
    }

    pub fn round_up(self) -> u32 {
        self.0.saturating_add(SCALE_BASE - 1) / SCALE_BASE
    }

    pub const fn from_wl(wl: u32) -> Self {
        Self(wl)
    }

    pub fn to_wl(self) -> u32 {
        self.0
    }

    #[inline(always)]
    pub fn pixel_size<const N: usize>(self, v: [i32; N]) -> [i32; N] {
        if self == Scale::default() {
            return v;
        }
        let scale = self.0 as i64;
        v.map(|v| ((v as i64 * scale + v.signum() as i64 * BASE64 / 2) / BASE64) as i32)
    }
}

impl PartialEq<u32> for Scale {
    fn eq(&self, other: &u32) -> bool {
        self.0 == other * SCALE_BASE
    }
}

impl Debug for Scale {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        Debug::fmt(&self.to_f64(), f)
    }
}

impl Display for Scale {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        Display::fmt(&self.to_f64(), f)
    }
}

#[derive(Default)]
pub struct Scales {
    scales: RefCounted<Scale>,
    indices_unused: RefCell<Bitfield>,
    indices: CopyHashMap<Scale, ScaleIndex, FxBuildHasher>,
}

#[jay_hash]
#[jay_clone(Copy)]
#[derive(Debug, Eq, Ord, PartialOrd)]
pub struct ScaleIndex(pub usize);

impl ScaleIndex {
    pub const INVALID: Self = Self(usize::MAX);
}

impl Scales {
    pub fn add(&self, scale: Scale) -> Option<ScaleIndex> {
        if self.scales.add(scale) {
            let idx = ScaleIndex(self.indices_unused.borrow_mut().acquire() as _);
            self.indices.set(scale, idx);
            Some(idx)
        } else {
            None
        }
    }

    pub fn remove(&self, scale: Scale) -> Option<ScaleIndex> {
        if self.scales.remove(&scale) {
            let idx = self.indices.remove(&scale).unwrap();
            self.indices_unused.borrow_mut().release(idx.0 as u32);
            Some(idx)
        } else {
            None
        }
    }

    pub fn get_index(&self, scale: Scale) -> ScaleIndex {
        self.indices.get(&scale).unwrap_or(ScaleIndex::INVALID)
    }

    #[expect(unused)]
    pub fn version(&self) -> u64 {
        self.scales.version()
    }

    pub fn lock(&self) -> copyhashmap::Locked<'_, Scale, ScaleIndex, FxBuildHasher> {
        self.indices.lock()
    }

    pub fn to_vec(&self) -> Vec<Scale> {
        self.scales.to_vec()
    }
}