diff options
| author | Micha White <botahamec@outlook.com> | 2024-08-15 20:14:15 -0400 |
|---|---|---|
| committer | Micha White <botahamec@outlook.com> | 2024-08-15 20:14:15 -0400 |
| commit | f8a80039c74332e2101a177ef3fde31ef2077224 (patch) | |
| tree | f887c96bf9879a28b7ce914ad96161f63ee83190 /packer | |
| parent | 488c7ed94b0662222fa0d825ab81b60b0b1e5d6c (diff) | |
Lots a changes
Diffstat (limited to 'packer')
| -rw-r--r-- | packer/packed.png | bin | 944566 -> 1268654 bytes | |||
| -rw-r--r-- | packer/src/bin/benchmark.rs | 12 | ||||
| -rw-r--r-- | packer/src/lib.rs | 58 |
3 files changed, 50 insertions, 20 deletions
diff --git a/packer/packed.png b/packer/packed.png Binary files differindex 1a913a2..9602cdb 100644 --- a/packer/packed.png +++ b/packer/packed.png 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<str>, + id: Arc<str>, x: u32, y: u32, - texture: Arc<RgbImage>, + texture: Arc<RgbaImage>, } #[derive(Debug, Clone, PartialEq, Eq)] -struct ImageRect(Arc<RgbImage>, Box<str>); +struct ImageRect(Arc<RgbaImage>, Arc<str>); #[derive(Debug, Default, Clone)] pub struct RectanglePacker { @@ -30,6 +30,12 @@ pub struct RectanglePacker { textures: Vec<ImageRect>, } +#[derive(Debug, Clone)] +pub struct TextureAtlas { + atlas: RgbaImage, + ids: HashMap<Arc<str>, Rectangle>, +} + impl PartialOrd for ImageRect { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) @@ -77,15 +83,19 @@ impl RectanglePacker { self.textures.shrink_to_fit() } - pub fn add_texture(&mut self, name: Box<str>, texture: Arc<RgbImage>) { + pub fn add_texture(&mut self, name: Arc<str>, texture: Arc<RgbaImage>) { if texture.width() > self.min_width { self.min_width = texture.width() + 1; } self.textures.push(ImageRect(texture, name)); } - fn pack(&mut self) -> (Vec<Texture>, u32, u32) { - let image_width = self.min_width; + fn pack(&mut self, min_width: u32) -> (Vec<Texture>, 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<Box<str>, 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<TextureAtlas, RawUnexpected> { + 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<Rectangle> { + self.ids.get(id).cloned() } } |
