1use image::{DynamicImage, RgbaImage};
2
3use crate::{format, Color, Point, Rect, Size};
4
5#[derive(Debug)]
7pub struct Packer {
8 image: RgbaImage,
9 bins: Bin,
10}
11
12#[derive(Debug)]
13struct Bin {
14 occupied: Option<Size>,
15 area: Rect,
16
17 children: Option<Box<(Bin, Bin)>>,
19}
20
21impl Packer {
22 pub fn new(size: Size) -> Self {
24 let mut image = RgbaImage::new(size.width, size.height);
25 image
26 .pixels_mut()
27 .for_each(|v| *v = Color::from([255, 0, 255, 0]));
28
29 Self {
30 image,
31 bins: Bin {
32 occupied: None,
33 children: None,
34 area: Rect::new(Point::new(0, 0), size),
35 },
36 }
37 }
38
39 pub fn pack(&mut self, image: &DynamicImage, gap: u32) -> Option<Rect> {
42 let size = Size::new(image.width(), image.height());
43 let gapped_size = Size::new(image.width() + gap * 2, image.height() + gap * 2);
44
45 match self.bins.insert(gapped_size) {
46 Some(rect) => {
47 let rect = Rect::new(Point::new(rect.min_x() + gap, rect.min_y() + gap), size);
48
49 let image = image.to_rgba8();
50 image.enumerate_pixels().for_each(|(x, y, p)| {
51 self.image.put_pixel(x + rect.min_x(), y + rect.min_y(), *p)
52 });
53
54 Some(rect)
55 }
56 None => None,
57 }
58 }
59
60 pub fn save(&self, name: &str, img: format::ImageFormat) -> anyhow::Result<()> {
62 self.image
63 .save_with_format(format!("{}.{}", name, img.ext()), img.as_image_format())?;
64 Ok(())
65 }
66}
67
68impl Bin {
69 pub fn fits(&self, size: Size) -> bool {
70 if self.area.width() < size.width || self.area.height() < size.height {
71 return false;
72 }
73
74 if let Some(children) = self.children.as_ref() {
75 return children.0.fits(size) || children.1.fits(size);
76 }
77
78 true
79 }
80
81 pub fn insert(&mut self, size: Size) -> Option<Rect> {
82 if !self.fits(size) {
83 return None;
84 }
85
86 if let Some(ref mut children) = self.children {
87 if let Some(rect) = children.0.insert(size) {
88 return Some(rect);
89 }
90
91 if let Some(rect) = children.1.insert(size) {
92 return Some(rect);
93 }
94
95 return None;
96 }
97
98 self.occupied = Some(size);
99 self.children = Some(Box::new((
100 Bin {
101 occupied: None,
102 area: Rect::new(
103 Point::new(self.area.min_x() + size.width, self.area.min_y()),
104 Size::new(self.area.width() - size.width, size.height),
105 ),
106 children: None,
107 },
108 Bin {
109 occupied: None,
110 area: Rect::new(
111 Point::new(self.area.min_x(), self.area.min_y() + size.height),
112 Size::new(self.area.width(), self.area.height() - size.height),
113 ),
114 children: None,
115 },
116 )));
117
118 Some(Rect::new(self.area.origin, size))
119 }
120}