summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMicha White <botahamec@outlook.com>2023-11-12 21:34:56 -0500
committerMicha White <botahamec@outlook.com>2023-11-12 21:34:56 -0500
commitec1f71ad69e9a286f6c68f51b3a3ec2a02dd14bb (patch)
tree3b160173f6187a88169bbaf37d69a359e0cf438c
parent279c233cb1f32ed42419ed6a9c2e14c1c1bc80e7 (diff)
Created a custom texture packer
-rw-r--r--packer/Cargo.toml10
-rw-r--r--packer/src/lib.rs145
2 files changed, 155 insertions, 0 deletions
diff --git a/packer/Cargo.toml b/packer/Cargo.toml
new file mode 100644
index 0000000..d370392
--- /dev/null
+++ b/packer/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "packer"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+image = "0.24"
+exun = "0.2"
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))
+ }
+}