fast image operations
Diffstat (limited to 'src/indexed.rs')
| -rw-r--r-- | src/indexed.rs | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/src/indexed.rs b/src/indexed.rs new file mode 100644 index 0000000..65e4359 --- /dev/null +++ b/src/indexed.rs @@ -0,0 +1,120 @@ +//! indexed images! whoo! (palette and `Image<[u8], 1>`, basically.) +#![allow(private_bounds)] +mod builder; + +use std::mem::MaybeUninit; + +use crate::Image; + +#[allow(non_camel_case_types)] +trait uint: Default + Copy + TryInto<usize> { + fn nat(self) -> usize { + self.try_into().ok().unwrap() + } +} + +macro_rules! int { + ($($t:ty)+) => { + $(impl uint for $t {})+ + }; +} +int!(u8 u16 u32 u64 usize u128); + +/// An image with a palette. +#[derive(Clone)] +pub struct IndexedImage<INDEX, PALETTE> { + // likely Box<[u8]>, … + // safety invariant: when INDEX<impl uint>, and PALETTE: Buffer, U must be < len(PALETTE) + buffer: Image<INDEX, 1>, + palette: PALETTE, // likely Box<[[f32; 4]]>, … +} + +impl<I, P> IndexedImage<I, P> { + /// Indexes the palette with each index. + pub fn to<PIXEL: Copy, INDEX: uint, const CHANNELS: usize>( + &self, + ) -> Image<Box<[PIXEL]>, CHANNELS> + where + P: AsRef<[[PIXEL; CHANNELS]]>, + I: AsRef<[INDEX]>, + { + unsafe { + self.buffer.map(|x| { + x.as_ref() + .iter() + // SAFETY: invariant. + .flat_map(|x| *self.palette.as_ref().get_unchecked(x.nat())) + .collect() + }) + } + } + + /// Sets the pixel. Does not check if the index is in bounds, nor if it is even a valid pixel. + pub unsafe fn set_unchecked<INDEX: Copy>(&mut self, x: u32, y: u32, pixel: INDEX) + where + I: AsMut<[INDEX]>, + { + // SAFETY: they checked! + unsafe { *self.pixel_mut(x, y) = pixel }; + } + + /// Gets a mutref to pixel. pls (is ub!) no set out of bound. + pub unsafe fn pixel_mut<INDEX: Copy>(&mut self, x: u32, y: u32) -> &mut INDEX + where + I: AsMut<[INDEX]>, + { + let p = self.buffer.at(x, y); + unsafe { self.raw().buffer.get_unchecked_mut(p) } + } + + /// Sets the pixel. Panics if bounds are not met. + pub fn set<INDEX: uint, PIXEL>(&mut self, x: u32, y: u32, pixel: INDEX) + where + I: AsMut<[INDEX]>, + P: AsRef<[PIXEL]>, + { + assert!(self.buffer.width() < x); + assert!(self.buffer.height() < x); + assert!(pixel.nat() < self.palette.as_ref().len()); + // SAFETY: we checked! + unsafe { self.set_unchecked(x, y, pixel) }; + } + + /// Gets a mut ref to raw parts. + pub unsafe fn raw<INDEX>(&mut self) -> Image<&mut [INDEX], 1> + where + I: AsMut<[INDEX]>, + { + self.buffer.as_mut() + } + + /// Provides the buffer and palette of this image. + pub fn into_raw_parts(self) -> (Image<I, 1>, P) { + (self.buffer, self.palette) + } + + /// Creates a indexed image from its 1 channel image representation and palette. + pub fn from_raw_parts<INDEX: uint, PIXEL>( + buffer: Image<I, 1>, + palette: P, + ) -> Result<Self, &'static str> + where + I: AsRef<[INDEX]>, + P: AsRef<[PIXEL]>, + { + let good = buffer.chunked().all(|[x]| x.nat() < palette.as_ref().len()); + good.then_some(Self { buffer, palette }) + .ok_or("not all indexes are in palette") + } +} + +impl<P, I: uint> IndexedImage<Box<[MaybeUninit<I>]>, P> { + /// Assumes this MU image is, in fact, initialized. You must be very sure that it is. Also, none of the pixels can be out of bounds. + pub unsafe fn assume_init(self) -> IndexedImage<Box<[I]>, P> { + IndexedImage { + // SAFETY: it really isnt. + buffer: unsafe { self.buffer.mapped(|x| x.assume_init()) }, + ..self + } + } +} |