use crate::pixel::Pixel;
use bevy::{prelude::*, render::render_resource::TextureUsages};
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
pub struct Frame<'a> {
pixels: &'a mut [Pixel],
size: UVec2,
}
impl<'a> Frame<'a> {
pub fn raw(&self) -> &[Pixel] {
self.pixels
}
pub fn raw_mut(&mut self) -> &mut [Pixel] {
self.pixels
}
pub fn size(&self) -> UVec2 {
self.size
}
pub fn per_pixel<P: Into<Pixel>>(&mut self, f: impl Fn(UVec2, Pixel) -> P) {
for (idx, pixel) in self.pixels.iter_mut().enumerate() {
let idx = idx as u32;
let pos = UVec2::new(idx % self.size.x, idx / self.size.x);
*pixel = f(pos, *pixel).into();
}
}
#[cfg(feature = "rayon")]
pub fn per_pixel_par<P: Into<Pixel>>(&mut self, f: impl Fn(UVec2, Pixel) -> P + Sync) {
self.pixels
.par_iter_mut()
.enumerate()
.for_each(|(idx, pixel)| {
let idx = idx as u32;
let pos = UVec2::new(idx % self.size.x, idx / self.size.x);
*pixel = f(pos, *pixel).into();
});
}
pub fn set(&mut self, location: impl Into<UVec2>, pixel: impl Into<Pixel>) -> FrameResult {
let location: UVec2 = location.into();
self.check_bounds(location)?;
let index = location.x + location.y * self.size.x;
self.pixels[index as usize] = pixel.into();
Ok(())
}
fn check_bounds(&self, location: UVec2) -> FrameResult {
if location.x >= self.size.x || location.y >= self.size.y {
Err(FrameError::LocationOutOfBounds {
location,
size: self.size,
})
} else {
Ok(())
}
}
}
pub type FrameResult = Result<(), FrameError>;
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum FrameError {
#[error(
"location out of the bounds of the frame (location: {location:?}, frame size: {size:?}"
)]
LocationOutOfBounds {
location: UVec2,
size: UVec2,
},
}
impl<'a> Frame<'a> {
pub fn get(image: &'a mut Image) -> Self {
debug_assert_eq!(image.texture_descriptor.format, Pixel::FORMAT);
debug_assert!(image
.texture_descriptor
.usage
.contains(TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST));
let size = image.size();
let pixels = bytemuck::cast_slice_mut(&mut image.data);
Self { pixels, size }
}
pub fn extract(images: &'a mut Assets<Image>, image_handle: &Handle<Image>) -> Self {
Self::get(
images
.get_mut(image_handle)
.expect("image when building frame"),
)
}
pub fn from_raw_parts(pixels: &'a mut [Pixel], size: UVec2) -> Self {
assert_eq!(pixels.len(), (size.x * size.y) as usize);
Self { pixels, size }
}
}
pub trait GetFrame {
fn frame(&mut self) -> Frame<'_>;
}
impl GetFrame for Image {
#[inline(always)]
fn frame(&mut self) -> Frame<'_> {
Frame::get(self)
}
}
pub trait GetFrameFromHandle: AsImageHandle {
fn frame<'a>(&self, images: &'a mut Assets<Image>) -> Frame<'a> {
Frame::extract(images, self.as_image_handle())
}
}
impl<T: AsImageHandle> GetFrameFromHandle for T {}
pub trait GetFrameFromImages: AsMut<Assets<Image>> {
fn frame(&mut self, image_handle: impl AsImageHandle) -> Frame<'_> {
Frame::extract(self.as_mut(), image_handle.as_image_handle())
}
}
impl<T: AsMut<Assets<Image>>> GetFrameFromImages for T {}
pub trait AsImageHandle {
fn as_image_handle(&self) -> &Handle<Image>;
}
impl AsImageHandle for Handle<Image> {
fn as_image_handle(&self) -> &Handle<Image> {
self
}
}
impl AsImageHandle for &Handle<Image> {
fn as_image_handle(&self) -> &Handle<Image> {
self
}
}
pub trait FrameEditExtension: GetFrame {
fn edit_frame(&mut self, f: impl Fn(&mut Frame)) {
f(&mut self.frame())
}
}
impl<T: GetFrame> FrameEditExtension for T {}