byte-engine 0.1.0

A composable Rust game engine focused on graphics, input, audio, physics, and retained UI.
use std::sync::{
	atomic::{AtomicU64, Ordering},
	Arc,
};

use crate::ui::{layout::Sizing, style::ConcreteStyle, Transform, Visual};

static NEXT_IMAGE_ID: AtomicU64 = AtomicU64::new(1);

#[derive(Clone)]
pub struct Image {
	id: u64,
	version: u64,
	width_pixels: u32,
	height_pixels: u32,
	pixels: Arc<[u8]>,
	pub width: Sizing,
	pub height: Sizing,
	pub(crate) style: ConcreteStyle,
	pub(crate) transform: Transform,
	pub(crate) visual: Visual,
}

impl Image {
	pub fn from_rgba(width: u32, height: u32, pixels: impl Into<Vec<u8>>) -> Self {
		let pixels = pixels.into();
		let expected_len = width as usize * height as usize * 4;
		assert_eq!(
			pixels.len(),
			expected_len,
			"RGBA image data must contain exactly width * height * 4 bytes"
		);

		Self {
			id: NEXT_IMAGE_ID.fetch_add(1, Ordering::Relaxed),
			version: 0,
			width_pixels: width,
			height_pixels: height,
			pixels: Arc::from(pixels),
			width: Sizing::Absolute(width as f32),
			height: Sizing::Absolute(height as f32),
			style: ConcreteStyle::default(),
			transform: Transform::default(),
			visual: Visual::default(),
		}
	}

	pub fn size(self, sizing: Sizing) -> Self {
		Self {
			width: sizing,
			height: sizing,
			..self
		}
	}

	pub fn width(self, width: Sizing) -> Self {
		Self { width, ..self }
	}

	pub fn height(self, height: Sizing) -> Self {
		Self { height, ..self }
	}

	pub fn style(mut self, style: impl Into<ConcreteStyle>) -> Self {
		self.style = style.into();
		self
	}

	pub fn transform(mut self, transform: impl Into<Transform>) -> Self {
		self.transform = transform.into();
		self
	}

	pub fn opacity(mut self, opacity: f32) -> Self {
		self.visual.opacity = opacity;
		self
	}

	pub fn set_rgba(&mut self, width: u32, height: u32, pixels: impl Into<Vec<u8>>) {
		let pixels = pixels.into();
		let expected_len = width as usize * height as usize * 4;
		assert_eq!(
			pixels.len(),
			expected_len,
			"RGBA image data must contain exactly width * height * 4 bytes"
		);

		self.width_pixels = width;
		self.height_pixels = height;
		self.pixels = Arc::from(pixels);
		self.version = self.version.wrapping_add(1);
	}

	pub fn set_style(&mut self, style: impl Into<ConcreteStyle>) {
		self.style = style.into();
	}

	pub fn set_transform(&mut self, transform: impl Into<Transform>) {
		self.transform = transform.into();
	}

	pub fn set_opacity(&mut self, opacity: f32) {
		self.visual.opacity = opacity;
	}

	pub fn id(&self) -> u64 {
		self.id
	}

	pub fn version(&self) -> u64 {
		self.version
	}

	pub fn width_pixels(&self) -> u32 {
		self.width_pixels
	}

	pub fn height_pixels(&self) -> u32 {
		self.height_pixels
	}

	pub fn pixels(&self) -> &[u8] {
		&self.pixels
	}

	pub fn style_ref(&self) -> &ConcreteStyle {
		&self.style
	}

	pub fn transform_ref(&self) -> &Transform {
		&self.transform
	}

	pub fn visual_ref(&self) -> &Visual {
		&self.visual
	}
}