extern crate image;
use self::image::{DynamicImage, GenericImageView, GenericImage};
use crate::math::rect::Rect;
use std::collections::HashMap;
use std::hash::Hash;
use crate::{Vect, Sprite};
pub struct Sheet<T: Hash + Eq + Clone + Copy> {
pub pic: DynamicImage,
pub regions: HashMap<T, Sprite>,
}
impl<T: Hash + Eq + Clone + Copy> Sheet<T> {
pub fn new(images: &mut [(T,DynamicImage)]) -> Self {
let mut data = vec![];
for i in 0..images.len() {
images[i].1 = images[i].1.flipv();
let img = &mut images[i];
data.push((img.0.clone(), img.1.width(), img.1.height()));
}
let mut image_set = HashMap::new();
for (key, img) in images {
image_set.insert(key, img);
}
let (width, height, data) = pack(data);
let mut regions = HashMap::new();
for (key, rect) in data {
regions.insert(key, rect);
}
let mut pic: DynamicImage = DynamicImage::new_rgba8(width, height);
for (key, img) in image_set.iter() {
let rect = regions.get(key).unwrap();
let offset = (rect.min.x as u32, rect.min.y as u32);
for (x, y, pix) in img.pixels() {
pic.put_pixel(x + offset.0, y + offset.1, pix);
}
}
Self {
pic,
regions: regions.into_iter().map(|(key, rect)| (key, Sprite::new(rect))).collect(),
}
}
}
pub fn pack<T: Hash + Eq + Clone + Copy>(mut data: Vec<(T, u32, u32)>) -> (u32, u32, Vec<(T, Rect)>) {
data.sort_by(|a, b| b.2.cmp(&a.2));
let count = data.len();
let start = {
let mut length = 0;
for i in data.iter() {
length += i.1;
}
let side = (length as f32).log2() as u32;
length = 0;
let mut i = 0;
while length < side {
length += data[i].1;
i += 1;
}
i
};
let mut point = start;
let mut other_way_around = false;
let mut lowest_ratio = i32::MAX;
let mut best = vec![];
let mut width = 0;
let mut height = 0;
'o: while point < count {
let mut len = 0;
for i in 0..point {
len += data[i].1;
}
if other_way_around {
for i in point..count {
if data[i].1 > len {
break 'o;
}
}
}
let mut current = point;
let mut breakpoints = vec![];
while current < count {
let mut total = 0;
breakpoints.push(current);
while current < count{
total += data[current].1;
if total > len {
break
}
current += 1;
}
}
let mut hight = data[0].2;
for i in breakpoints.iter() {
hight += data[*i].2
}
let ratio = (len * hight) as i32;
if ratio < lowest_ratio {
lowest_ratio = ratio;
best = breakpoints;
width = len;
height = hight;
} else if ratio > lowest_ratio {
if other_way_around {
break;
}
other_way_around = true;
point = start;
}
if point == 0 {
break;
}
if other_way_around {point -= 1} else {point += 1};
}
let mut data: Vec<(T, Rect)> = data.into_iter().map(|(key, w, h)| (key, rect!(0, 0, w, h))).collect();
best.insert(0, 0);
best.push(data.len());
let mut offset = Vect::ZERO;
for i in 0..best.len() - 1 {
for i in best[i]..best[i + 1] {
data[i].1 = data[i].1.moved(offset);
offset.x += data[i].1.width()
}
offset.y += data[best[i]].1.height();
offset.x = 0.0;
}
(width, height, data)
}