diff options
| -rw-r--r-- | engine/src/image.rs | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/engine/src/image.rs b/engine/src/image.rs index ac95f22..b6d8013 100644 --- a/engine/src/image.rs +++ b/engine/src/image.rs @@ -1,8 +1,10 @@ +use std::any::type_name; use std::fs::File; use std::io::BufReader; use std::path::Path; use image_rs::GenericImageView as _; +use zerocopy::{FromBytes, Immutable}; use crate::color::Color; use crate::data_types::dimens::Dimens; @@ -33,6 +35,32 @@ impl Image }) } + pub fn try_from_bytes( + bytes: &[u8], + dimens: Dimens<u32>, + color_type: ColorType, + ) -> Result<Self, FromBytesError> + { + use image_rs::{Luma, LumaA, Rgb, Rgba}; + + let inner = match color_type { + ColorType::L8 => image_buf_from_bytes::<Luma<u8>>(dimens, bytes)?.into(), + ColorType::La8 => image_buf_from_bytes::<LumaA<u8>>(dimens, bytes)?.into(), + ColorType::Rgb8 => image_buf_from_bytes::<Rgb<u8>>(dimens, bytes)?.into(), + ColorType::Rgba8 => image_buf_from_bytes::<Rgba<u8>>(dimens, bytes)?.into(), + ColorType::L16 => image_buf_from_bytes::<Luma<u16>>(dimens, bytes)?.into(), + ColorType::La16 => image_buf_from_bytes::<LumaA<u16>>(dimens, bytes)?.into(), + ColorType::Rgb16 => image_buf_from_bytes::<Rgb<u16>>(dimens, bytes)?.into(), + ColorType::Rgba16 => image_buf_from_bytes::<Rgba<u16>>(dimens, bytes)?.into(), + ColorType::Rgb32F => image_buf_from_bytes::<Rgb<f32>>(dimens, bytes)?.into(), + ColorType::Rgba32F => { + image_buf_from_bytes::<Rgba<f32>>(dimens, bytes)?.into() + } + }; + + Ok(Self { inner }) + } + pub fn from_color(dimens: impl Into<Dimens<u32>>, color: impl Into<Color<u8>>) -> Self { @@ -173,3 +201,72 @@ pub enum Error #[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct DecodeError(image_rs::ImageError); + +#[derive(Debug, thiserror::Error)] +pub enum FromBytesError +{ + #[error( + "The number of bytes provided is not enough to fit a image with the given size" + )] + NotEnoughBytesForDimensions, + + #[error("Failed to cast bytes to subpixels")] + CastToSubPixelsFailed(#[from] CastToSubPixelsError), +} + +#[derive(Debug, thiserror::Error)] +pub enum CastToSubPixelsError +{ + #[error( + "Source address {:?} ({}) isn't a multiple of the alignment of the type {} ({})", + src, + src_type_name, + dst_type_name, + dst_align + )] + Alignment + { + src_type_name: &'static str, + dst_type_name: &'static str, + src: *const u8, + dst_align: usize, + }, + + #[error( + "Source size {} ({}) is incorrect size for type {}", + src_size, + src_type_name, + dst_type_name + )] + Size + { + src_type_name: &'static str, + dst_type_name: &'static str, + src_size: usize, + }, +} + +fn image_buf_from_bytes<Pixel>( + dimens: Dimens<u32>, + bytes: &[u8], +) -> Result<image_rs::ImageBuffer<Pixel, Vec<Pixel::Subpixel>>, FromBytesError> +where + Pixel: image_rs::Pixel<Subpixel: FromBytes + Immutable>, +{ + let buf = <[Pixel::Subpixel]>::ref_from_bytes(bytes).map_err(|err| match err { + zerocopy::CastError::Alignment(_) => CastToSubPixelsError::Alignment { + src_type_name: type_name::<&[u8]>(), + dst_type_name: type_name::<&[Pixel]>(), + src: bytes.as_ptr(), + dst_align: align_of_val::<[Pixel]>(&[]), + }, + zerocopy::ConvertError::Size(_) => CastToSubPixelsError::Size { + src_type_name: type_name::<&[u8]>(), + dst_type_name: type_name::<&[Pixel]>(), + src_size: bytes.len(), + }, + })?; + + image_rs::ImageBuffer::from_raw(dimens.width, dimens.height, buf.to_vec()) + .ok_or(FromBytesError::NotEnoughBytesForDimensions) +} |
