From f8a80039c74332e2101a177ef3fde31ef2077224 Mon Sep 17 00:00:00 2001 From: Micha White Date: Thu, 15 Aug 2024 20:14:15 -0400 Subject: Lots a changes --- packer/src/bin/benchmark.rs | 12 ++++++---- packer/src/lib.rs | 58 +++++++++++++++++++++++++++++++++------------ 2 files changed, 50 insertions(+), 20 deletions(-) (limited to 'packer/src') diff --git a/packer/src/bin/benchmark.rs b/packer/src/bin/benchmark.rs index bb83992..4d898d5 100644 --- a/packer/src/bin/benchmark.rs +++ b/packer/src/bin/benchmark.rs @@ -10,17 +10,19 @@ fn main() -> Result<(), exun::RawUnexpected> { let start = Instant::now(); let mut packer = RectanglePacker::new(); - packer.add_texture("gator".into(), Arc::new(img1.to_rgb8())); - packer.add_texture("bunny".into(), Arc::new(img2.to_rgb8())); - packer.add_texture("ghost".into(), Arc::new(img3.to_rgb8())); + packer.add_texture("gator".into(), Arc::new(img1.to_rgba8())); + packer.add_texture("bunny".into(), Arc::new(img2.to_rgba8())); + packer.add_texture("ghost".into(), Arc::new(img3.to_rgba8())); println!("{} milliseconds", start.elapsed().as_secs_f32() * 1000.0); let start = Instant::now(); - let packed = packer.output(); + let packed = packer.output(1, 1); println!("{} milliseconds", start.elapsed().as_secs_f32() * 1000.0); let mut file = File::create("packed.png")?; - packed?.0.write_to(&mut file, ImageOutputFormat::Bmp)?; + packed? + .get_full_atlas() + .write_to(&mut file, ImageOutputFormat::Bmp)?; Ok(()) } diff --git a/packer/src/lib.rs b/packer/src/lib.rs index 328f90e..6715a9e 100644 --- a/packer/src/lib.rs +++ b/packer/src/lib.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use std::sync::Arc; use exun::RawUnexpected; -use image::{GenericImage, RgbImage}; +use image::{GenericImage, RgbaImage}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Rectangle { @@ -15,14 +15,14 @@ pub struct Rectangle { #[derive(Debug, Clone, PartialEq, Eq)] struct Texture { - id: Box, + id: Arc, x: u32, y: u32, - texture: Arc, + texture: Arc, } #[derive(Debug, Clone, PartialEq, Eq)] -struct ImageRect(Arc, Box); +struct ImageRect(Arc, Arc); #[derive(Debug, Default, Clone)] pub struct RectanglePacker { @@ -30,6 +30,12 @@ pub struct RectanglePacker { textures: Vec, } +#[derive(Debug, Clone)] +pub struct TextureAtlas { + atlas: RgbaImage, + ids: HashMap, Rectangle>, +} + impl PartialOrd for ImageRect { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) @@ -77,15 +83,19 @@ impl RectanglePacker { self.textures.shrink_to_fit() } - pub fn add_texture(&mut self, name: Box, texture: Arc) { + pub fn add_texture(&mut self, name: Arc, texture: Arc) { if texture.width() > self.min_width { self.min_width = texture.width() + 1; } self.textures.push(ImageRect(texture, name)); } - fn pack(&mut self) -> (Vec, u32, u32) { - let image_width = self.min_width; + fn pack(&mut self, min_width: u32) -> (Vec, u32, u32) { + let image_width = self.min_width.max(min_width); + + // to make sure padding is rounded up and not down, 64 is added + // to make sure some padding is always present, minimum is 1 pixel + let horizontal_padding = ((image_width + 64) / 128).max(1); let mut x_position = 0; let mut y_position = 0; @@ -97,7 +107,9 @@ impl RectanglePacker { for texture in &self.textures { // loop to the next row if we've gone off the edge if (x_position + texture.0.width()) > image_width { - y_position += largest_row_height; + let vertical_padding = ((largest_row_height + 64) / 128).max(1); + + y_position += largest_row_height + vertical_padding; x_position = 0; largest_row_height = 0; } @@ -106,7 +118,7 @@ impl RectanglePacker { let x = x_position; let y = y_position; - x_position += texture.0.width(); + x_position += texture.0.width() + horizontal_padding; if texture.0.height() > largest_row_height { largest_row_height = texture.0.height(); @@ -120,17 +132,23 @@ impl RectanglePacker { }); } - let total_height = y_position + largest_row_height; + let vertical_padding = ((largest_row_height + 64) / 128).max(1); + let total_height = y_position + largest_row_height + vertical_padding; (rectangles, image_width, total_height) } - pub fn output(&mut self) -> Result<(RgbImage, HashMap, Rectangle>), RawUnexpected> { - let (rectangles, image_width, image_height) = self.pack(); - let mut image = RgbImage::new(image_width, image_height); + pub fn output( + &mut self, + min_width: u32, + min_height: u32, + ) -> Result { + let (rectangles, image_width, image_height) = self.pack(min_width); + let image_height = image_height.max(min_height); + let mut atlas = RgbaImage::new(image_width, image_height); let mut ids = HashMap::with_capacity(rectangles.len()); for rectangle in rectangles { - image.copy_from(rectangle.texture.deref(), rectangle.x, rectangle.y)?; + atlas.copy_from(rectangle.texture.deref(), rectangle.x, rectangle.y)?; ids.insert( rectangle.id, Rectangle { @@ -142,6 +160,16 @@ impl RectanglePacker { ); } - Ok((image, ids)) + Ok(TextureAtlas { atlas, ids }) + } +} + +impl TextureAtlas { + pub fn get_full_atlas(&self) -> &RgbaImage { + &self.atlas + } + + pub fn get_texture_rect(&self, id: &str) -> Option { + self.ids.get(id).cloned() } } -- cgit v1.2.3