use std::error::Error;
use std::sync::atomic::{AtomicUsize, Ordering};
use image::error::DecodingError;
use image::{DynamicImage, ImageError};
use texture_packer::{
exporter::{ExportResult, ImageExporter},
MultiTexturePacker, TexturePackerConfig,
};
use thiserror::Error;
static NEXT_TEXTURE_ID: AtomicUsize = AtomicUsize::new(0);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
struct TextureId(usize);
impl TextureId {
pub fn new() -> Self {
Self(NEXT_TEXTURE_ID.fetch_add(1, Ordering::Relaxed))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ImageFormat {
Bmp,
Ico,
Farbfeld,
}
impl ImageFormat {
const fn format(self) -> image::ImageFormat {
match self {
Self::Bmp => image::ImageFormat::Bmp,
Self::Ico => image::ImageFormat::Ico,
Self::Farbfeld => image::ImageFormat::Farbfeld,
}
}
}
type PackError = impl std::fmt::Debug;
#[derive(Error, Debug)]
pub enum TextureError {
#[error("{:?}", .0)]
TextureTooLarge(PackError), // use an error with a source
#[error("{}", .0)]
BadImage(#[source] DecodingError), // TODO don't export this
#[error("Unexpected Error (this is a bug in alligator_render): {}", .0)]
Unexpected(#[source] Box<dyn Error>),
}
impl From<ImageError> for TextureError {
fn from(ie: ImageError) -> Self {
match ie {
ImageError::Decoding(de) => Self::BadImage(de),
_ => Self::Unexpected(Box::new(ie)),
}
}
}
struct TextureAtlases<'a> {
packer: MultiTexturePacker<'a, image::RgbaImage, TextureId>,
}
impl<'a> Default for TextureAtlases<'a> {
fn default() -> Self {
Self {
packer: MultiTexturePacker::new_skyline(TexturePackerConfig::default()),
}
}
}
impl<'a> TextureAtlases<'a> {
/// Creates a new texture atlas, with the given size
// TODO why is this u32?
pub fn new(width: u32, height: u32) -> Self {
Self {
packer: MultiTexturePacker::new_skyline(TexturePackerConfig {
max_width: width,
max_height: height,
..Default::default()
}),
}
}
// TODO support RGBA16
pub fn load_from_memory(
&mut self,
buf: &[u8],
format: ImageFormat,
) -> Result<TextureId, TextureError> {
let img = image::load_from_memory_with_format(buf, format.format())?.into_rgba8();
let id = TextureId::new();
self.packer
.pack_own(id, img)
.map_err(TextureError::TextureTooLarge)?;
Ok(id)
}
pub(crate) fn atlases(&self) -> ExportResult<Box<[DynamicImage]>> {
self.packer
.get_pages()
.iter()
.map(ImageExporter::export)
.collect::<ExportResult<Vec<DynamicImage>>>()
.map(Vec::into_boxed_slice)
}
}
|