use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use exun::{Expect, Expected, Unexpected};
use thiserror::Error;
static NEXT_TEXTURE_ID: AtomicUsize = AtomicUsize::new(0);
type Rgba16Texture = image::ImageBuffer<image::Rgba<u16>, Box<[u16]>>;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TextureId(usize);
impl TextureId {
fn new() -> Self {
Self(NEXT_TEXTURE_ID.fetch_add(1, Ordering::Relaxed))
}
}
/// These are the formats supported by the renderer.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ImageFormat {
Bmp,
Ico,
Farbfeld,
}
impl From<ImageFormat> for image::ImageFormat {
fn from(format: ImageFormat) -> Self {
match format {
ImageFormat::Bmp => Self::Bmp,
ImageFormat::Ico => Self::Ico,
ImageFormat::Farbfeld => Self::Farbfeld,
}
}
}
#[derive(Debug, Error)]
#[error("{}", .0)]
pub struct DecodingError(#[from] image::error::DecodingError);
type LoadError = Expect<DecodingError>;
pub struct TextureManager {
textures: HashMap<TextureId, Rgba16Texture>,
}
#[allow(clippy::missing_const_for_fn)]
fn convert_image_decoding(e: image::ImageError) -> Expect<DecodingError> {
if let image::ImageError::Decoding(de) = e {
Expected(de.into())
} else {
Unexpected(e.into())
}
}
impl TextureManager {
#[must_use]
pub fn new() -> Self {
Self {
textures: HashMap::new(),
}
}
/// Loads a texture from memory in the given format.
///
/// # Errors
///
/// This returns `Expected(DecodingError)` if the given buffer was invalid
/// for the given format.
#[allow(clippy::missing_panics_doc)]
pub fn load_from_memory(
&mut self,
buf: &[u8],
format: ImageFormat,
) -> Result<TextureId, LoadError> {
let id = TextureId::new();
let texture = image::load_from_memory_with_format(buf, format.into())
.map_err(convert_image_decoding)?;
let texture = texture.into_rgb16();
let width = texture.width();
let height = texture.height();
let buf = texture.into_raw().into_boxed_slice();
let texture = image::ImageBuffer::from_raw(width, height, buf).unwrap();
self.textures.insert(id, texture);
Ok(id)
}
}
|