mirui 0.44.0

A lightweight, no_std ECS-driven UI framework for embedded, desktop, and WebAssembly
Documentation
use alloc::vec::Vec;
use core::cell::RefCell;

pub mod demos;

pub(crate) struct SceneRgbaScratch(RefCell<Vec<u8>>);

impl SceneRgbaScratch {
    #[cfg(feature = "std")]
    pub(crate) fn new(bytes: usize) -> Self {
        Self(RefCell::new(alloc::vec![0; bytes]))
    }

    #[cfg(feature = "std")]
    pub(crate) fn ensure_capacity(&mut self, bytes: usize) {
        if self.0.get_mut().len() < bytes {
            self.0.get_mut().resize(bytes, 0);
        }
    }

    pub(crate) fn with_surface_rgba<R>(
        &self,
        rect: crate::types::Rect,
        scale: crate::types::Fixed,
        f: impl FnOnce(&mut [u8]) -> R,
    ) -> R {
        let width = usize::try_from(
            (rect.w.max(crate::types::Fixed::ZERO) * scale)
                .ceil()
                .to_int(),
        )
        .unwrap_or(0);
        let height = usize::try_from(
            (rect.h.max(crate::types::Fixed::ZERO) * scale)
                .ceil()
                .to_int(),
        )
        .unwrap_or(0);
        let required = width
            .checked_mul(height)
            .and_then(|pixels| pixels.checked_mul(4))
            .unwrap_or(0);
        let mut rgba = self.0.borrow_mut();
        if rgba.len() < required {
            rgba.resize(required, 0);
        }
        f(rgba.as_mut_slice())
    }

    #[cfg(feature = "std")]
    pub(crate) fn install(world: &mut crate::ecs::World, bytes: usize) {
        if let Some(scratch) = world.resource_mut::<Self>() {
            scratch.ensure_capacity(bytes);
        } else {
            world.insert_resource(Self::new(bytes));
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::types::{Fixed, Rect};

    #[test]
    fn surface_scratch_grows_for_physical_pixels_without_shrinking() {
        let scratch = SceneRgbaScratch::new(16);
        let rect = Rect {
            x: Fixed::ZERO,
            y: Fixed::ZERO,
            w: Fixed::from_int(3),
            h: Fixed::from_int(2),
        };

        scratch.with_surface_rgba(rect, Fixed::from_int(2), |rgba| {
            assert_eq!(rgba.len(), 6 * 4 * 4);
        });
        scratch.with_surface_rgba(rect, Fixed::ONE, |rgba| {
            assert_eq!(rgba.len(), 6 * 4 * 4);
        });
    }
}