summaryrefslogtreecommitdiff
path: root/packer/src
diff options
context:
space:
mode:
Diffstat (limited to 'packer/src')
-rw-r--r--packer/src/bin/benchmark.rs12
-rw-r--r--packer/src/lib.rs58
2 files changed, 50 insertions, 20 deletions
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()
}
}