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), } impl From 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 { 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> { self.packer .get_pages() .iter() .map(ImageExporter::export) .collect::>>() .map(Vec::into_boxed_slice) } }