summaryrefslogtreecommitdiff
path: root/engine/src/image.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2025-05-25 17:34:45 +0200
committerHampusM <hampus@hampusmat.com>2025-06-03 14:08:40 +0200
commita109ac00e67129ddb358b6416a3241f22a441ad9 (patch)
treeb0d245c01ef4be675aacfcabd6ccfc6b7cd5889e /engine/src/image.rs
parent68a97b0123da1f181d1c78a7f760c55c3f341014 (diff)
feat(engine): add asset management
Diffstat (limited to 'engine/src/image.rs')
-rw-r--r--engine/src/image.rs184
1 files changed, 184 insertions, 0 deletions
diff --git a/engine/src/image.rs b/engine/src/image.rs
new file mode 100644
index 0000000..a4513ae
--- /dev/null
+++ b/engine/src/image.rs
@@ -0,0 +1,184 @@
+use std::fs::File;
+use std::io::BufReader;
+use std::path::Path;
+
+use image_rs::GenericImageView as _;
+
+use crate::asset::{Assets, Submitter as AssetSubmitter};
+use crate::color::Color;
+use crate::data_types::dimens::Dimens;
+use crate::util::builder;
+
+#[derive(Debug)]
+pub struct Image
+{
+ inner: image_rs::DynamicImage,
+}
+
+impl Image
+{
+ pub fn open(path: impl AsRef<Path>) -> Result<Self, Error>
+ {
+ let buffered_reader =
+ BufReader::new(File::open(&path).map_err(Error::ReadFailed)?);
+
+ let image_reader = image_rs::io::Reader::with_format(
+ buffered_reader,
+ image_rs::ImageFormat::from_path(path)
+ .map_err(|_| Error::UnsupportedFormat)?,
+ );
+
+ Ok(Self {
+ inner: image_reader
+ .decode()
+ .map_err(|err| Error::DecodeFailed(DecodeError(err)))?,
+ })
+ }
+
+ pub fn from_color(dimens: impl Into<Dimens<u32>>, color: impl Into<Color<u8>>)
+ -> Self
+ {
+ let dimens: Dimens<u32> = dimens.into();
+
+ let color: Color<u8> = color.into();
+
+ Self {
+ inner: image_rs::RgbImage::from_pixel(
+ dimens.width,
+ dimens.height,
+ image_rs::Rgb([color.red, color.green, color.blue]),
+ )
+ .into(),
+ }
+ }
+
+ pub fn dimensions(&self) -> Dimens<u32>
+ {
+ self.inner.dimensions().into()
+ }
+
+ pub fn color_type(&self) -> ColorType
+ {
+ self.inner.color().into()
+ }
+
+ pub fn as_bytes(&self) -> &[u8]
+ {
+ self.inner.as_bytes()
+ }
+}
+
+builder! {
+#[builder(name = SettingsBuilder, derives=(Debug, Clone))]
+#[derive(Debug, Default, Clone)]
+#[non_exhaustive]
+pub struct Settings {
+}
+}
+
+impl Settings
+{
+ pub fn builder() -> SettingsBuilder
+ {
+ SettingsBuilder::default()
+ }
+}
+
+impl Default for SettingsBuilder
+{
+ fn default() -> Self
+ {
+ Settings::default().into()
+ }
+}
+
+/// An enumeration over supported color types and bit depths
+#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
+#[non_exhaustive]
+pub enum ColorType
+{
+ /// Pixel is 8-bit luminance
+ L8,
+
+ /// Pixel is 8-bit luminance with an alpha channel
+ La8,
+
+ /// Pixel contains 8-bit R, G and B channels
+ Rgb8,
+
+ /// Pixel is 8-bit RGB with an alpha channel
+ Rgba8,
+
+ /// Pixel is 16-bit luminance
+ L16,
+
+ /// Pixel is 16-bit luminance with an alpha channel
+ La16,
+
+ /// Pixel is 16-bit RGB
+ Rgb16,
+
+ /// Pixel is 16-bit RGBA
+ Rgba16,
+
+ /// Pixel is 32-bit float RGB
+ Rgb32F,
+
+ /// Pixel is 32-bit float RGBA
+ Rgba32F,
+}
+
+impl From<image_rs::ColorType> for ColorType
+{
+ fn from(color_type: image_rs::ColorType) -> Self
+ {
+ match color_type {
+ image_rs::ColorType::L8 => Self::L8,
+ image_rs::ColorType::La8 => Self::La8,
+ image_rs::ColorType::Rgb8 => Self::Rgb8,
+ image_rs::ColorType::Rgba8 => Self::Rgba8,
+ image_rs::ColorType::L16 => Self::L16,
+ image_rs::ColorType::La16 => Self::La16,
+ image_rs::ColorType::Rgb16 => Self::Rgb16,
+ image_rs::ColorType::Rgba16 => Self::Rgba16,
+ image_rs::ColorType::Rgb32F => Self::Rgb32F,
+ image_rs::ColorType::Rgba32F => Self::Rgba32F,
+ _ => {
+ panic!("Unrecognized image_rs::ColorType variant");
+ }
+ }
+ }
+}
+
+pub fn set_asset_importers(assets: &mut Assets)
+{
+ assets.set_importer::<_, _>(["png", "jpg"], import_asset);
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ #[error("Failed to read image file")]
+ ReadFailed(#[source] std::io::Error),
+
+ #[error("Failed to decode image")]
+ DecodeFailed(DecodeError),
+
+ #[error("Unsupported image format")]
+ UnsupportedFormat,
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error(transparent)]
+pub struct DecodeError(image_rs::ImageError);
+
+fn import_asset(
+ asset_submitter: &mut AssetSubmitter<'_>,
+ path: &Path,
+ _settings: Option<&'_ Settings>,
+) -> Result<(), Error>
+{
+ asset_submitter.submit_store::<Image>(Image::open(path)?);
+
+ Ok(())
+}