diff options
| author | Micha White <botahamec@outlook.com> | 2023-11-12 21:34:56 -0500 |
|---|---|---|
| committer | Micha White <botahamec@outlook.com> | 2023-11-12 21:34:56 -0500 |
| commit | ec1f71ad69e9a286f6c68f51b3a3ec2a02dd14bb (patch) | |
| tree | 3b160173f6187a88169bbaf37d69a359e0cf438c /packer/src | |
| parent | 279c233cb1f32ed42419ed6a9c2e14c1c1bc80e7 (diff) | |
Created a custom texture packer
Diffstat (limited to 'packer/src')
| -rw-r--r-- | packer/src/lib.rs | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/packer/src/lib.rs b/packer/src/lib.rs new file mode 100644 index 0000000..06c66ae --- /dev/null +++ b/packer/src/lib.rs @@ -0,0 +1,145 @@ +use std::sync::Arc; +use std::{collections::HashMap, ops::Deref}; + +use exun::RawUnexpected; +use image::{GenericImage, RgbImage}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Rectangle { + pub x: u32, + pub y: u32, + pub width: u32, + pub height: u32, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct Texture { + id: Box<str>, + x: u32, + y: u32, + texture: Arc<RgbImage>, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct ImageRect(Arc<RgbImage>, Box<str>); + +#[derive(Debug, Default, Clone)] +pub struct RectanglePacker { + min_width: u32, + textures: Vec<ImageRect>, +} + +impl PartialOrd for ImageRect { + fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for ImageRect { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.0.height().cmp(&other.0.height()) + } +} + +impl RectanglePacker { + pub fn new() -> Self { + Self { + min_width: 1, + textures: Vec::new(), + } + } + + pub fn with_capacity(capacity: usize) -> Self { + Self { + min_width: 1, + textures: Vec::with_capacity(capacity), + } + } + + pub fn capacity(&self) -> usize { + self.textures.capacity() + } + + pub fn len(&self) -> usize { + self.textures.len() + } + + pub fn is_empty(&self) -> bool { + self.textures.is_empty() + } + + pub fn reserve(&mut self, additional: usize) { + self.textures.reserve(additional) + } + + pub fn shrink_to_fit(&mut self) { + self.textures.shrink_to_fit() + } + + pub fn add_texture(&mut self, name: Box<str>, texture: Arc<RgbImage>) { + 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; + + let mut x_position = 0; + let mut y_position = 0; + let mut largest_row_height = 0; + let mut rectangles = Vec::with_capacity(self.textures.len()); + + self.textures.sort(); + 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; + x_position = 0; + largest_row_height = 0; + } + + // set the rectangle position + let x = x_position; + let y = y_position; + + x_position += texture.0.width(); + + if texture.0.height() > largest_row_height { + largest_row_height = texture.0.height(); + } + + rectangles.push(Texture { + id: texture.1.clone(), + x, + y, + texture: texture.0.clone(), + }); + } + + let total_height = y_position + largest_row_height; + + (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); + let mut ids = HashMap::with_capacity(rectangles.len()); + for rectangle in rectangles { + image.copy_from(rectangle.texture.deref(), rectangle.x, rectangle.y)?; + ids.insert( + rectangle.id, + Rectangle { + x: rectangle.x, + y: rectangle.y, + width: rectangle.texture.width(), + height: rectangle.texture.height(), + }, + ); + } + + Ok((image, ids)) + } +} |
