#![warn(missing_docs)]
extern crate framing;
use framing::{AsBytes, Image, Chunky};
use std::{mem, ptr};
pub struct Atlas<T> {
bytes: Vec<u8>,
scratch: Vec<u8>,
width: usize,
height: usize,
blank: T,
rects: Vec<Rect>
}
impl<T> Atlas<T> {
pub fn new(blank: T) -> Self {
Atlas {
bytes: Vec::new(),
scratch: Vec::new(),
width: 0,
height: 0,
blank: blank,
rects: Vec::new()
}
}
pub fn add<U>(&mut self, image: U) -> (usize, usize)
where
T: AsBytes + Clone + Sync + 'static,
U: Image<Pixel = T> + Sync
{
let (w, h) = (image.width(), image.height());
if w == 0 || h == 0 {
return (0, 0);
}
if self.width == 0 || self.height == 0 {
self.bytes.reserve(T::width() * w * h);
self.width = w;
self.height = h;
for (_, _, pixel) in framing::iter(&image) {
self.bytes.extend_from_slice(T::Bytes::from(pixel).as_ref())
}
return (0, 0);
}
let result = self.rects.iter()
.enumerate()
.filter(|&(_, rect)| w <= rect.w && h <= rect.h)
.min_by_key(|&(_, rect)| {
let (dw, dh) = (rect.w - w, rect.h - h);
if dh < dw { dh } else { dw }
})
.map(|(i, rect)| (i, rect.clone()));
if let Some((i, rect)) = result {
self.rects.remove(i);
if rect.w != w {
self.rects.push(Rect {
x: rect.x + w,
y: rect.y,
w: rect.w - w,
h: h
});
}
if rect.h != h {
self.rects.push(Rect {
x: rect.x,
y: rect.y + h,
w: w,
h: rect.h - h
});
}
if rect.w != w && rect.h != h {
self.rects.push(Rect {
x: rect.x + w,
y: rect.y + h,
w: rect.w - w,
h: rect.h - h
});
}
for y in 0..h {
for x in 0..w {
let i = T::width() * (self.width * (rect.y + y) + (rect.x + x));
let p = T::Bytes::from(unsafe {
image.pixel(x, y)
});
unsafe {
ptr::copy_nonoverlapping(
p.as_ref().as_ptr(),
self.bytes.as_mut_ptr().offset(i as isize),
T::width()
)
}
}}
(rect.x, rect.y)
} else {
if self.height <= self.width {
if self.width > w {
self.rects.push(Rect {
x: w,
y: self.height,
w: self.width - w,
h: h
});
} else if self.width < w {
self.rects.push(Rect {
x: self.width,
y: 0,
w: w - self.width,
h: self.height
});
}
if w <= self.width {
self.bytes.reserve(T::width() * self.width * h);
for y in self.height..(self.height + h) {
for x in 0..w {
let pixel = T::Bytes::from(unsafe {
image.pixel(x, y)
});
self.bytes.extend_from_slice(pixel.as_ref());
}
for _ in w..self.width {
let pixel = T::Bytes::from(self.blank.clone());
self.bytes.extend_from_slice(pixel.as_ref());
}
}
self.height = self.height + h;
} else {
let cap = T::width() * (self.height + h) * w;
self.scratch.clear();
self.scratch.reserve(cap);
for chunk in self.bytes.chunks(T::width() * self.width) {
self.scratch.extend_from_slice(chunk);
for _ in self.width..w {
let pixel = T::Bytes::from(self.blank.clone());
self.scratch.extend_from_slice(pixel.as_ref());
}
}
for y in 0..h {
for x in 0..w {
let pixel = T::Bytes::from(unsafe {
image.pixel(x, y)
});
self.scratch.extend_from_slice(pixel.as_ref());
}
}
mem::swap(&mut self.bytes, &mut self.scratch);
self.width = w;
self.height = self.height + h;
}
(0, self.height)
} else {
if self.height > h {
self.rects.push(Rect {
x: self.width,
y: h,
w: w,
h: self.height - h
});
} else if self.height < h {
self.rects.push(Rect {
x: 0,
y: self.height,
w: self.width,
h: h - self.height
});
}
let new_height = if self.height <= h { h } else { self.height };
let new_width = self.width + w;
let cap = T::width() * new_width * new_height;
self.scratch.clear();
self.scratch.reserve(cap);
for (y, chunk) in
self.bytes
.chunks(T::width() * self.width)
.enumerate()
{
self.scratch.extend_from_slice(chunk);
if y < h {
for x in 0..w {
let pixel = T::Bytes::from(unsafe {
image.pixel(x, y)
});
self.scratch.extend_from_slice(pixel.as_ref());
}
} else {
for _ in 0..w {
let pixel = T::Bytes::from(self.blank.clone());
self.scratch.extend_from_slice(pixel.as_ref());
}
}
}
for y in self.height..h {
for _ in 0..self.width {
let pixel = T::Bytes::from(self.blank.clone());
self.scratch.extend_from_slice(pixel.as_ref());
}
for x in 0..w {
let pixel = T::Bytes::from(unsafe {
image.pixel(x, y)
});
self.scratch.extend_from_slice(pixel.as_ref());
}
}
mem::swap(&mut self.bytes, &mut self.scratch);
self.width = new_width;
self.height = new_height;
(self.width, 0)
}
}
}
}
impl<T> Into<Chunky<T>> for Atlas<T> where T: AsBytes {
fn into(self) -> Chunky<T> {
Chunky::from_bytes(self.width, self.height, self.bytes)
}
}
impl<T> Image for Atlas<T> where T: AsBytes {
type Pixel = T;
fn width(&self) -> usize {
self.width
}
fn height(&self) -> usize {
self.height
}
unsafe fn pixel(&self, x: usize, y: usize) -> Self::Pixel {
let off = T::width() * (y * self.width + x);
let mut bytes = T::Bytes::default();
ptr::copy_nonoverlapping(
self.bytes.as_ptr().offset(off as isize),
bytes.as_mut().as_mut_ptr(),
T::width()
);
bytes.into()
}
}
#[derive(Clone)]
struct Rect {
x: usize,
y: usize,
w: usize,
h: usize
}