summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engine/Cargo.toml2
-rw-r--r--engine/src/image.rs58
-rw-r--r--engine/src/windowing/window.rs55
3 files changed, 108 insertions, 7 deletions
diff --git a/engine/Cargo.toml b/engine/Cargo.toml
index 67f59d8..1dc6431 100644
--- a/engine/Cargo.toml
+++ b/engine/Cargo.toml
@@ -28,7 +28,7 @@ features = ["rwh_06", "wayland", "wayland-dlopen", "x11"]
[dependencies.image_rs]
version = "0.25.10"
default-features = false
-features = ["png", "jpeg"]
+features = ["png", "jpeg", "ico"]
package = "image"
[dependencies.zerocopy]
diff --git a/engine/src/image.rs b/engine/src/image.rs
index dbcad9d..51ba459 100644
--- a/engine/src/image.rs
+++ b/engine/src/image.rs
@@ -1,6 +1,6 @@
use std::any::type_name;
use std::fs::File;
-use std::io::BufReader;
+use std::io::{BufRead, BufReader, Seek};
use std::path::Path;
use image_rs::GenericImageView as _;
@@ -19,8 +19,7 @@ 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 buffered_reader = BufReader::new(File::open(&path).map_err(Error::Io)?);
let image_reader = image_rs::ImageReader::with_format(
buffered_reader,
@@ -35,6 +34,20 @@ impl Image
})
}
+ pub fn from_reader(reader: impl BufRead + Seek, format: Format)
+ -> Result<Self, Error>
+ {
+ let image_reader =
+ image_rs::ImageReader::with_format(reader, format.into_image_rs());
+
+ Ok(Self {
+ inner: image_reader
+ .decode()
+ // TODO: err might be other errors than decode errors here
+ .map_err(|err| Error::DecodeFailed(DecodeError(err)))?,
+ })
+ }
+
pub fn try_from_bytes(
bytes: &[u8],
dimens: Dimens<u32>,
@@ -121,10 +134,24 @@ impl Image
Self { inner: self.inner.to_rgba8().into() }
}
+ /// Consumes the image and returns a RGBA8 image. If it already is RGBA8, the image is
+ /// returned as is. If not, the image is converted to RGBA8.
+ pub fn into_rgba8(self) -> Self
+ {
+ Self {
+ inner: self.inner.into_rgba8().into(),
+ }
+ }
+
pub fn as_bytes(&self) -> &[u8]
{
self.inner.as_bytes()
}
+
+ pub fn into_bytes(self) -> Vec<u8>
+ {
+ self.inner.into_bytes()
+ }
}
/// An enumeration over supported color types and bit depths
@@ -185,11 +212,32 @@ impl From<image_rs::ColorType> for ColorType
}
}
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
+#[non_exhaustive]
+pub enum Format
+{
+ Png,
+ Jpeg,
+ Ico,
+}
+
+impl Format
+{
+ fn into_image_rs(self) -> image_rs::ImageFormat
+ {
+ match self {
+ Self::Png => image_rs::ImageFormat::Png,
+ Self::Jpeg => image_rs::ImageFormat::Jpeg,
+ Self::Ico => image_rs::ImageFormat::Ico,
+ }
+ }
+}
+
#[derive(Debug, thiserror::Error)]
pub enum Error
{
- #[error("Failed to read file")]
- ReadFailed(#[source] std::io::Error),
+ #[error("I/O error")]
+ Io(#[source] std::io::Error),
#[error("Failed to decode image")]
DecodeFailed(DecodeError),
diff --git a/engine/src/windowing/window.rs b/engine/src/windowing/window.rs
index 67554ca..8f95dc8 100644
--- a/engine/src/windowing/window.rs
+++ b/engine/src/windowing/window.rs
@@ -1,6 +1,7 @@
use std::borrow::Cow;
use crate::ecs::Component;
+use crate::image::Image;
use crate::reflection::Reflection;
use crate::windowing::dpi::{PhysicalSize, Position, Size};
@@ -32,6 +33,8 @@ pub struct CreationAttributes
pub resizable: bool,
pub position: Option<Position>,
pub inner_size: Option<Size>,
+ pub icon: Option<Icon>,
+
pub x_visual_id: Option<XVisualID>,
// These do not have equivalents in winit::window::WindowAttributes
@@ -62,6 +65,8 @@ gen_creation_attrs_with_fn!(visible, bool);
gen_creation_attrs_with_fn!(resizable, bool);
gen_creation_attrs_with_fn!(position, Option<Position>);
gen_creation_attrs_with_fn!(inner_size, Option<Size>);
+gen_creation_attrs_with_fn!(icon, Option<Icon>);
+
gen_creation_attrs_with_fn!(x_visual_id, Option<XVisualID>);
gen_creation_attrs_with_fn!(cursor_visible, bool);
@@ -69,6 +74,7 @@ gen_creation_attrs_with_fn!(cursor_grab_mode, CursorGrabMode);
impl CreationAttributes
{
+ #[tracing::instrument(skip_all)]
pub(crate) fn into_window_attrs(self) -> winit::window::WindowAttributes
{
let mut window_attrs = winit::window::WindowAttributes::default()
@@ -82,7 +88,42 @@ impl CreationAttributes
None => None,
})
.with_visible(self.visible)
- .with_resizable(self.resizable);
+ .with_resizable(self.resizable)
+ .with_window_icon(match self.icon {
+ Some(Icon::Image(image)) => {
+ let image_dimens = image.dimensions();
+
+ let result = winit::window::Icon::from_rgba(
+ image.into_rgba8().into_bytes(),
+ image_dimens.width,
+ image_dimens.height,
+ );
+
+ if let Err(err) = &result {
+ tracing::error!("Invalid icon image: {err}");
+ }
+
+ result.ok()
+ }
+ #[cfg(windows)]
+ Some(Icon::WindowsResource(resource_name)) => {
+ use winit::platform::windows::IconExtWindows;
+
+ let result = winit::window::Icon::from_resource_name(
+ resource_name.as_ref(),
+ None,
+ );
+
+ if let Err(err) = &result {
+ tracing::error!(
+ "Invalid icon image in resource {resource_name}: {err}"
+ );
+ }
+
+ result.ok()
+ }
+ None => None,
+ });
window_attrs.position = self.position.map(Into::into);
window_attrs.inner_size = self.inner_size.map(Into::into);
@@ -116,6 +157,7 @@ impl Default for CreationAttributes
resizable: true,
position: None,
inner_size: None,
+ icon: None,
x_visual_id: None,
cursor_visible: true,
cursor_grab_mode: CursorGrabMode::None,
@@ -130,6 +172,17 @@ pub enum Fullscreen
Borderless,
}
+/// Window icon
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub enum Icon
+{
+ Image(Image),
+
+ #[cfg(windows)]
+ WindowsResource(Cow<'static, str>),
+}
+
#[derive(Debug, Component, Clone, Copy)]
pub struct CreationReady;