pel 0.1.0

OpenGL backed framebuffer
Documentation
use std::ops::{Deref, DerefMut};

use crate::{Error, Pel};

/// Surface used by pel [`Texture`][1].
///
/// This type has exactly the same layout as texture used internally by pel that is used for data
/// uploads to GPU.
///
/// It is "native" format of pel - `ARGB` buffer backed by `Vec<u32>`.
///
/// [1]: struct.TextureHandle.html
#[derive(Clone)]
pub struct Surface(RawSurface<Pel>);

impl Surface {
    /// Surface constructor
    pub fn new(w: usize, h: usize) -> Self {
        Self(RawSurface::new(w, h))
    }

    /// Decomposes surface into its raw components.
    ///
    /// * buf - a vector of BGRA bytes;
    /// * w - surface width in pels;
    /// * h - surface height in pels.
    pub fn into_raw_parts(self) -> (Vec<Pel>, usize, usize) {
        self.0.into_raw_parts()
    }

    /// Creates surface directly from the raw components of another surface.
    ///
    /// # Panics
    ///
    /// If `buf.len()` isn't equal to `w * h`.
    pub fn from_raw_parts(buf: Vec<Pel>, w: usize, h: usize) -> Self {
        Self(RawSurface::from_raw_parts(buf, w, h))
    }

    /// Creates surface directly from the raw components of another surface without checking if
    /// buffer size is correct.
    ///
    /// # Safety
    ///
    /// If `buf.len()` isn't equal to `w * h`, ***undefined behaviour*** may be invoked by later
    /// operations on this surface.
    pub unsafe fn from_raw_parts_unchecked(buf: Vec<Pel>, w: usize, h: usize) -> Self {
        Self(RawSurface::from_raw_parts_unchecked(buf, w, h))
    }
}

impl Deref for Surface {
    type Target = RawSurface<Pel>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for Surface {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

/// Raw, generic surface used by pel.
#[derive(Clone)]
pub struct RawSurface<I> {
    buf: Vec<I>,
    w: usize,
    h: usize,
}

impl<I> RawSurface<I>
where
    I: Default + Copy,
{
    /// Surface constructor.
    pub fn new(w: usize, h: usize) -> Self {
        let buf = vec![I::default(); w * h];
        Self { buf, w, h }
    }

    /// Surface width.
    pub fn width(&self) -> usize {
        self.w
    }

    /// Surface height.
    pub fn height(&self) -> usize {
        self.h
    }

    /// Surface size as a (width, height) tuple.
    pub fn size(&self) -> (usize, usize) {
        (self.w, self.h)
    }

    /// Decomposes surface into its raw components.
    ///
    /// * buf - a vector of BGRA bytes;
    /// * w - surface width in pels;
    /// * h - surface height in pels.
    pub fn into_raw_parts(self) -> (Vec<I>, usize, usize) {
        (self.buf, self.w, self.h)
    }

    /// Creates surface directly from the raw components of another surface.
    ///
    /// # Panics
    ///
    /// If `buf.len()` isn't equal to `w * h`.
    pub fn from_raw_parts(buf: Vec<I>, w: usize, h: usize) -> Self {
        if buf.len() != w * h {
            panic!("Cannot form surface from malformed buffer");
        } else {
            Self { buf, w, h }
        }
    }

    /// Creates surface directly from the raw components of another surface without checking if
    /// buffer size is correct.
    ///
    /// # Safety
    ///
    /// If `buf.len()` isn't equal to `w * h`, ***undefined behaviour*** may be invoked by later
    /// operations on this surface.
    pub unsafe fn from_raw_parts_unchecked(buf: Vec<I>, w: usize, h: usize) -> Self {
        Self { buf, w, h }
    }

    /// Returns a raw pointer to the surface's internal buffer.
    ///
    /// The caller must ensure that the surface outlives the pointer this function returns, or else
    /// it will end up pointing to garbage. The caller must also ensure that the memory the
    /// pointer (non-transitively) points to is never written to (except inside an
    /// [`UnsafeCell`][1]) using this pointer or any pointer derived from it.
    ///
    /// [1]: https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html
    pub fn as_ptr(&self) -> *const I {
        self.buf.as_ptr()
    }

    /// Returns an unsafe mutable pointer to the surface's internal buffer.
    ///
    /// The caller must ensure that the surface outlives the pointer this function returns, or else
    /// it will end up pointing to garbage.
    pub fn as_mut_ptr(&mut self) -> *mut I {
        self.buf.as_mut_ptr()
    }

    /// Extracts a slice referencing entire underlying buffer.
    pub fn as_slice(&self) -> &[I] {
        self.buf.as_slice()
    }

    /// Extracts a mutable slice referencing entire underlying buffer.
    pub fn as_mut_slice(&mut self) -> &mut [I] {
        self.buf.as_mut_slice()
    }

    /// Clears entire surface with pel value.
    pub fn clear<O: Into<I>>(&mut self, value: O) {
        let value = value.into();
        for color in self.buf.iter_mut() {
            *color = value;
        }
    }

    /// Transforms all pels using given function.
    pub fn for_each<F, O>(&mut self, f: F)
    where
        F: Fn(O) -> O,
        O: From<I> + Into<I>,
    {
        for color in self.buf.iter_mut() {
            let old_pel = (*color).into();
            let new_pel = f(old_pel);
            *color = new_pel.into();
        }
    }

    /// Reads pel value.
    ///
    /// # Errors
    ///
    /// If indices are out of surface bounds, [`Error::OutOfBounds`][1] is returned.
    ///
    /// [1]: enum.Error.html#variant.OutOfBounds
    pub fn read<O: From<I>>(&self, x: usize, y: usize) -> crate::Result<O> {
        let index = self.index(x, y)?;
        let color = unsafe { self.buf.get_unchecked(index) };
        Ok((*color).into())
    }

    /// Writes pel value.
    ///
    /// # Errors
    ///
    /// If indices are out of surface bounds, [`Error::OutOfBounds`][1] is returned.
    ///
    /// [1]: enum.Error.html#variant.OutOfBounds
    pub fn write<O: Into<I>>(&mut self, x: usize, y: usize, value: O) -> crate::Result<()> {
        let index = self.index(x, y)?;
        let color = unsafe { self.buf.get_unchecked_mut(index) };
        *color = value.into();
        Ok(())
    }

    /// Reads pel value.
    ///
    /// # Safety
    ///
    /// It is ***undefined behaviour*** to provide indices that are out of surface bounds.
    pub unsafe fn read_unchecked<O: From<I>>(&self, x: usize, y: usize) -> O {
        let index = self.index_unchecked(x, y);
        let color = self.buf.get_unchecked(index);
        (*color).into()
    }

    /// Writes pel value.
    ///
    /// # Safety
    ///
    /// It is ***undefined behaviour*** to provide indices that are out of surface bounds.
    pub unsafe fn write_unchecked<O: Into<I>>(&mut self, x: usize, y: usize, value: O) {
        let index = self.index_unchecked(x, y);
        let color = self.buf.get_unchecked_mut(index);
        *color = value.into();
    }

    /// Calculates index of a pel at (x, y) of this surface.
    ///
    /// (0, 0) is top left of the surface.
    ///
    /// # Errors
    ///
    /// If indices are out of surface bounds, [`Error::OutOfBounds`][1] is returned.
    ///
    /// [1]: enum.Error.html#variant.OutOfBounds
    pub fn index(&self, x: usize, y: usize) -> crate::Result<usize> {
        match self.index_unchecked(x, y) {
            index if index < self.buf.len() => Ok(index),
            _ => Err(Error::OutOfBounds {
                upper_bound: (self.w, self.h),
                indices: (x, y),
            }),
        }
    }

    /// Calculates index of a pel at (x, y) of this surface.
    ///
    /// (0, 0) is top left of the surface.
    ///
    /// This function doesn't check if index is in bounds, but is still safe.
    pub fn index_unchecked(&self, x: usize, y: usize) -> usize {
        y * self.w + x
    }
}