From 20b0a802225da2668263c93b492a49d25714598e Mon Sep 17 00:00:00 2001 From: Micha White Date: Sat, 19 Nov 2022 12:23:15 -0500 Subject: Created TextureRef --- alligator_resources/src/texture.rs | 160 +++++++++++++++++++++++++++---------- 1 file changed, 116 insertions(+), 44 deletions(-) (limited to 'alligator_resources/src/texture.rs') diff --git a/alligator_resources/src/texture.rs b/alligator_resources/src/texture.rs index f1d34fc..9aca56d 100644 --- a/alligator_resources/src/texture.rs +++ b/alligator_resources/src/texture.rs @@ -2,12 +2,15 @@ use std::collections::HashMap; use std::mem::{self, MaybeUninit}; use std::path::Path; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; use image::{GenericImage, ImageBuffer}; use texture_packer::exporter::ImageExporter; use texture_packer::{Frame, TexturePacker, TexturePackerConfig}; use thiserror::Error; +use crate::Priority; + static NEXT_TEXTURE_ID: AtomicUsize = AtomicUsize::new(0); #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -82,12 +85,12 @@ fn texture_size(image: &Rgba16Texture) -> usize { image.len() * mem::size_of::>() } -struct ImageFile { +struct TextureFile { path: Box, - texture: Option, + texture: Option>, } -impl ImageFile { +impl TextureFile { #[allow(clippy::missing_const_for_fn)] fn new(path: impl AsRef) -> Self { Self { @@ -97,21 +100,21 @@ impl ImageFile { } fn open(path: impl AsRef) -> Result { - let texture = image::open(&path).map_err(convert_image_load_error)?; - let texture = texture.to_rgba16(); - let texture = vec_image_to_box(texture); + let mut this = Self::new(path); + this.load()?; - Ok(Self { - path: path.as_ref().into(), - texture: Some(texture), - }) + Ok(this) + } + + const fn is_loaded(&self) -> bool { + self.texture.is_some() } fn load(&mut self) -> Result<&Rgba16Texture, LoadError> { if self.texture.is_none() { let texture = image::open(&self.path).map_err(convert_image_load_error)?; let texture = texture.to_rgba16(); - let texture = vec_image_to_box(texture); + let texture = Arc::new(vec_image_to_box(texture)); self.texture = Some(texture); } @@ -123,14 +126,99 @@ impl ImageFile { } fn allocated_size(&self) -> usize { - self.texture - .as_ref() - .map_or(0, |texture| texture.len() * mem::size_of::()) + self.texture.as_ref().map_or(0, |t| texture_size(t)) + } +} + +enum TextureBuffer { + Memory(Arc), + Disk(TextureFile), +} + +struct Texture { + priority: Priority, + buffer: TextureBuffer, +} + +impl Texture { + fn from_buffer(texture: Rgba16Texture) -> Self { + Self { + priority: Priority::Urgent, + buffer: TextureBuffer::Memory(Arc::new(texture)), + } + } + + fn from_path(path: impl AsRef, priority: Priority) -> Self { + Self { + priority, + buffer: TextureBuffer::Disk(TextureFile::new(path)), + } + } + + const fn priority(&self) -> Priority { + self.priority + } + + fn set_priority(&mut self, priority: Priority) { + self.priority = priority; + } + + fn load_texture(&mut self) -> Result<&Rgba16Texture, LoadError> { + match &mut self.buffer { + TextureBuffer::Memory(ref texture) => Ok(texture), + TextureBuffer::Disk(file) => file.load(), + } + } +} + +pub struct TextureRef<'a> { + id: TextureId, + texture: Arc, + manager: &'a TextureManager, +} + +impl<'a> TextureRef<'a> { + fn texture_width(&self) -> u32 { + self.texture.width() + } + + fn texture_height(&self) -> u32 { + self.texture.height() + } + + #[allow(clippy::missing_const_for_fn)] + fn texture(&self) -> &Rgba16Texture { + &self.texture + } + + // TODO: it's safer to replace this with a position thingy + #[must_use] + pub fn atlas_x(&self) -> f32 { + self.manager + .subtexture_x(self.id) + .expect("not in texture atlas") + } + + #[must_use] + pub fn atlas_y(&self) -> f32 { + self.manager + .subtexture_y(self.id) + .expect("not in texture atlas") + } + + #[must_use] + pub fn width(&self) -> f32 { + self.manager.texture_width(self) + } + + #[must_use] + pub fn height(&self) -> f32 { + self.manager.texture_height(self) } } pub struct TextureManager { - textures: HashMap, + textures: HashMap, packer: TexturePacker<'static, Rgba16Texture, TextureId>, atlas: Rgba16Texture, // cached texture atlas width: u32, @@ -179,32 +267,22 @@ impl TextureManager { } } - pub fn load_to_atlas(&mut self, id: TextureId) { - let texture = self.texture(id); - if self.packer.pack_own(id, texture.clone()).is_err() { - let texture = self.texture(id); + pub fn load_to_atlas(&mut self, id: TextureId, texture: &TextureRef) { + let get_texture = || texture.texture().clone(); + + if self.packer.pack_own(id, get_texture()).is_err() { + let texture = get_texture(); self.resize_atlas(self.width + texture.width(), self.height + texture.height()); - let texture = self.texture(id); self.packer - .pack_own(id, texture.clone()) + .pack_own(id, get_texture()) .expect("packer is still too small after resizing"); } } - /// Resize the texture atlas + /// Clear and resize the texture atlas pub fn resize_atlas(&mut self, width: u32, height: u32) { - let old_packer = &self.packer; - let mut new_packer = packer(width, height); - - for id in old_packer.get_frames().keys() { - let texture = self.texture(*id).clone(); - new_packer - .pack_own(*id, texture) - .expect("resized packer is too small to hold subtextures"); - } - - self.packer = new_packer; + self.packer = packer(width, height); } /// Clear the texture atlas @@ -228,6 +306,7 @@ impl TextureManager { let texture = texture.map_err(convert_image_decoding)?; let texture = texture.into_rgba16(); let texture = vec_image_to_box(texture); + let texture = Texture::from_buffer(texture); self.textures.insert(id, texture); @@ -237,7 +316,6 @@ impl TextureManager { pub fn atlas(&mut self) -> &Rgba16Texture { let atlas = { profiling::scope!("export atlas"); - // TODO unexpect_msg? ImageExporter::export(&self.packer).expect("ImageExporter error?") }; @@ -249,10 +327,6 @@ impl TextureManager { &self.atlas } - fn texture(&self, id: TextureId) -> &Rgba16Texture { - self.textures.get(&id).expect("invalid TextureId") - } - /// Get the subtexture in the texture atlas fn subtexture(&self, id: TextureId) -> Option<&Frame> { self.packer.get_frame(&id) @@ -277,16 +351,14 @@ impl TextureManager { /// Get the width of a texture #[must_use] #[allow(clippy::cast_precision_loss)] - pub fn texture_width(&self, id: TextureId) -> f32 { - let width = self.texture(id).width(); - width as f32 / self.width as f32 + pub fn texture_width(&self, texture: &TextureRef) -> f32 { + texture.texture_width() as f32 / self.width as f32 } /// Get the height of a texture #[must_use] #[allow(clippy::cast_precision_loss)] - pub fn texture_height(&self, id: TextureId) -> f32 { - let height = self.texture(id).height(); - height as f32 / self.height as f32 + pub fn texture_height(&self, texture: &TextureRef) -> f32 { + texture.texture_height() as f32 / self.height as f32 } } -- cgit v1.2.3