feim 0.25.1

Modular crate for working with images in Rust
Documentation
use crate::color::convert::{ConvertFrom, ConvertInto};
use crate::color::{Color, Zero};
use crate::image::{Dimensions, Image, ImageMut};

pub trait AsTyped {
    type Pixel: Color;

    fn as_typed(&self) -> &[Self::Pixel];
}

pub trait AsTypedMut {
    type Pixel: Color;

    fn as_typed_mut(&mut self) -> &mut [Self::Pixel];
}

#[derive(Clone, Debug)]
pub struct RawPixBuf<T> {
    width: usize,
    height: usize,
    buf: Box<[T]>,
}

impl<T> RawPixBuf<T> {
    pub fn new(width: usize, height: usize) -> Self
    where
        T: Zero,
    {
        let buf = {
            let buf = vec![const { T::ZERO }; width * height];
            buf.into_boxed_slice()
        };
        RawPixBuf { width, height, buf }
    }

    pub fn new_from_dims<D: Dimensions>(dims: &D) -> Self
    where
        T: Zero,
    {
        let (width, height) = dims.dimensions();
        Self::new(width, height)
    }

    #[inline]
    pub fn from_vec(width: usize, height: usize, buf: Vec<u8>) -> Result<Self, Vec<u8>> {
        let slice = buf.into_boxed_slice();
        Self::from_boxed_slice(width, height, slice).map_err(Vec::from)
    }

    pub fn from_boxed_slice(
        width: usize,
        height: usize,
        buf: Box<[u8]>,
    ) -> Result<Self, Box<[u8]>> {
        let expected_len = width * height * std::mem::size_of::<T>();
        if buf.len() != expected_len
            || !(buf.as_ptr() as usize).is_multiple_of(std::mem::align_of::<T>())
        {
            return Err(buf);
        }
        #[allow(clippy::cast_slice_from_raw_parts)]
        let buf = unsafe {
            let ptr: *mut T = Box::into_raw(buf) as _;
            Box::from_raw(std::slice::from_raw_parts_mut(ptr, width * height))
        };
        Ok(RawPixBuf { width, height, buf })
    }
}

impl<T> RawPixBuf<T> {
    #[inline]
    pub fn into_pixels(self) -> Vec<T> {
        self.buf.into_vec()
    }
}

impl<C: Color> AsTyped for RawPixBuf<C> {
    type Pixel = C;

    #[inline]
    fn as_typed(&self) -> &[C] {
        self.buf.as_ref()
    }
}

impl<C: Color> AsTypedMut for RawPixBuf<C> {
    type Pixel = C;

    #[inline]
    fn as_typed_mut(&mut self) -> &mut [C] {
        self.buf.as_mut()
    }
}

impl<T> AsRef<[u8]> for RawPixBuf<T> {
    #[inline]
    fn as_ref(&self) -> &[u8] {
        unsafe {
            let len = self.buf.len() * std::mem::size_of::<T>();
            let ptr: *const u8 = self.buf.as_ptr() as _;
            std::slice::from_raw_parts(ptr, len)
        }
    }
}

impl<T> AsMut<[u8]> for RawPixBuf<T> {
    #[inline]
    fn as_mut(&mut self) -> &mut [u8] {
        unsafe {
            let len = self.buf.len() * std::mem::size_of::<T>();
            let ptr: *mut u8 = self.buf.as_mut_ptr() as _;
            std::slice::from_raw_parts_mut(ptr, len)
        }
    }
}

impl<T> Dimensions for RawPixBuf<T> {
    #[inline]
    fn width(&self) -> usize {
        self.width
    }

    #[inline]
    fn height(&self) -> usize {
        self.height
    }
}

impl<C: Copy + Color> Image for RawPixBuf<C> {
    type Pixel = C;

    #[inline]
    fn color_get(&self, x: usize, y: usize) -> C {
        let width = self.width();
        let buffer = self.as_typed();
        buffer[y * width + x]
    }
}

impl<C: Color> ImageMut for RawPixBuf<C> {
    type Pixel = C;

    #[inline]
    fn color_set<P, ColorSpecialized>(&mut self, x: usize, y: usize, color: P)
    where
        P: ConvertInto<C, ColorSpecialized> + Color,
    {
        let width = self.width();
        let buffer = self.as_typed_mut();
        let color: C = color.convert_into();
        buffer[y * width + x] = color;
    }
}

impl<C1, C2> From<&RawPixBuf<C1>> for RawPixBuf<C2>
where
    C1: Color + Copy,
    C2: Color + Zero + ConvertFrom<C1>,
    RawPixBuf<C1>: Dimensions,
{
    fn from(buffer: &RawPixBuf<C1>) -> RawPixBuf<C2> {
        let (width, height) = buffer.dimensions();
        let mut new_buffer = RawPixBuf::new(width, height);

        for y in 0..height {
            for x in 0..width {
                new_buffer.color_set(x, y, buffer.color_get(x, y));
            }
        }

        new_buffer
    }
}