From 180a3206aae57d16493e6268d816083c2c2fbd91 Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 19 Apr 2026 20:51:08 +0200 Subject: feat(engine): add image::Image::try_from_bytes fn --- engine/src/image.rs | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) (limited to 'engine') 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, + color_type: ColorType, + ) -> Result + { + use image_rs::{Luma, LumaA, Rgb, Rgba}; + + let inner = match color_type { + ColorType::L8 => image_buf_from_bytes::>(dimens, bytes)?.into(), + ColorType::La8 => image_buf_from_bytes::>(dimens, bytes)?.into(), + ColorType::Rgb8 => image_buf_from_bytes::>(dimens, bytes)?.into(), + ColorType::Rgba8 => image_buf_from_bytes::>(dimens, bytes)?.into(), + ColorType::L16 => image_buf_from_bytes::>(dimens, bytes)?.into(), + ColorType::La16 => image_buf_from_bytes::>(dimens, bytes)?.into(), + ColorType::Rgb16 => image_buf_from_bytes::>(dimens, bytes)?.into(), + ColorType::Rgba16 => image_buf_from_bytes::>(dimens, bytes)?.into(), + ColorType::Rgb32F => image_buf_from_bytes::>(dimens, bytes)?.into(), + ColorType::Rgba32F => { + image_buf_from_bytes::>(dimens, bytes)?.into() + } + }; + + Ok(Self { inner }) + } + pub fn from_color(dimens: impl Into>, color: impl Into>) -> 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( + dimens: Dimens, + bytes: &[u8], +) -> Result>, FromBytesError> +where + Pixel: image_rs::Pixel, +{ + 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) +} -- cgit v1.2.3-18-g5258