summaryrefslogtreecommitdiff
path: root/engine/src
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-04-19 20:51:08 +0200
committerHampusM <hampus@hampusmat.com>2026-04-19 20:51:08 +0200
commit180a3206aae57d16493e6268d816083c2c2fbd91 (patch)
tree3ac1125fce617ab4e2fb92c8af50ef9d8a389b40 /engine/src
parent83505ac5a4b917f80c81560f04258edbd0e92dde (diff)
feat(engine): add image::Image::try_from_bytes fn
Diffstat (limited to 'engine/src')
-rw-r--r--engine/src/image.rs97
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)
+}