Browse Source

textium: Implement the skyline atlas packer

master
Erin 8 years ago
parent
commit
2b6109fc3e
2 changed files with 200 additions and 0 deletions
  1. +5
    -0
      textium/src/packer/mod.rs
  2. +195
    -0
      textium/src/packer/skyline.rs

+ 5
- 0
textium/src/packer/mod.rs View File

@ -1,6 +1,10 @@
mod skyline;
use ::texture::{Buffer2d, ResizeableBuffer2d};
use ::geometry::Rect;
pub use self::skyline::SkylinePacker;
pub trait Packer {
type Buffer: Buffer2d;
@ -18,6 +22,7 @@ pub trait Packer {
}
pub trait GrowingPacker: Packer {
/// `resize_fn` should describe a strategy for growing the atlas buffer based on the current size.
fn pack_resize<F, O>(&mut self, buf: &O, resize_fn: F) -> Rect<usize>
where O: Buffer2d<Pixel=<<Self as Packer>::Buffer as Buffer2d>::Pixel>,
F: Fn((usize, usize)) -> (usize, usize);


+ 195
- 0
textium/src/packer/skyline.rs View File

@ -0,0 +1,195 @@
use std::cmp::max;
use ::texture::Buffer2d;
use ::geometry::Rect;
use super::Packer;
struct Skyline {
pub x: usize,
pub y: usize,
pub w: usize,
}
pub struct SkylinePacker<B> where B: Buffer2d {
buf: B,
width: usize,
height: usize,
skylines: Vec<Skyline>,
margin: usize,
}
impl<B> SkylinePacker<B> where B: Buffer2d {
fn find_skyline(&self, w: usize, h: usize) -> Option<(usize, Rect<usize>)> {
let mut min_height = ::std::usize::MAX;
let mut min_width = ::std::usize::MAX;
let mut index = None;
let mut rect: Rect<usize> = Rect::new(0, 0, 0, 0);
// Note: Try to keep the min_height as small as possible
for i in 0..self.skylines.len() {
if let Some(y) = self.can_place(i, w, h) {
if y + h < min_height ||
(y + h == min_height && self.skylines[i].w < min_width) {
min_height = y+h;
min_width = self.skylines[i].w;
index = Some(i);
rect.x = self.skylines[i].x;
rect.y = y;
rect.w = w;
rect.h = h;
}
}
}
if let Some(i) = index {
Some((i, rect))
} else {
None
}
}
fn can_place(&self, i: usize, w: usize, h: usize) -> Option<usize> {
let x = self.skylines[i].x;
if x + w > self.width {
return None;
}
let mut width_left = w;
let mut i = i;
let mut y = self.skylines[i].y;
loop {
y = max(y, self.skylines[i].y);
if y+h > self.height {
return None;
}
if self.skylines[i].w > width_left {
return Some(y);
}
width_left -= self.skylines[i].w;
i += 1;
if i >= self.skylines.len() {
return None;
}
}
}
fn split(&mut self, index: usize, rect: &Rect<usize>) {
let skyline = Skyline {
x: rect.x,
y: rect.y + rect.h,
w: rect.w
};
assert!(skyline.x + skyline.w <= self.width);
assert!(skyline.y <= self.height);
self.skylines.insert(index, skyline);
let i = index + 1;
while i < self.skylines.len() {
assert!(self.skylines[i-1].x <= self.skylines[i].x);
if self.skylines[i].x < self.skylines[i-1].x + self.skylines[i-1].w {
let shrink = self.skylines[i-1].x + self.skylines[i-1].w - self.skylines[i].x;
if self.skylines[i].w <= shrink {
self.skylines.remove(i);
} else {
self.skylines[i].x += shrink;
self.skylines[i].w -= shrink;
break;
}
} else {
break;
}
}
}
/// Merge skylines with the same y value.
fn merge(&mut self) {
let mut i = 1;
while i < self.skylines.len() {
if self.skylines[i-1].y == self.skylines[i].y {
self.skylines[i-1].w += self.skylines[i].w;
self.skylines.remove(i);
i -= 1;
}
i += 1;
}
}
}
impl<B> Packer for SkylinePacker<B> where B: Buffer2d {
type Buffer = B;
fn dimensions(&self) -> (usize, usize) {(self.width, self.height)}
fn set_dimensions(&mut self, w: usize, h: usize) {
let (old_w, _) = self.dimensions();
self.width = w;
self.height = h;
// create a "ceiling" which fills in the skyline data structure
// with a line at y=0 from the old width to the new width.
self.skylines.push(Skyline {
x: old_w, y: 0,
w: w-old_w,
});
}
fn set_margin(&mut self, margin: usize) {
self.margin = margin;
}
fn new(buf: B) -> SkylinePacker<B> {
let (w, h) = buf.dimensions();
let mut skylines = Vec::new();
// Fill with a single skyline.
skylines.push(Skyline {
x: 0, y: 0,
w: w
});
SkylinePacker {
buf: buf,
width: w, height: h,
skylines: skylines,
margin: 0,
}
}
fn pack<O>(&mut self, buf: &O) -> Option<Rect<usize>>
where O: Buffer2d<Pixel=B::Pixel>
{
let (mut w, mut h) = buf.dimensions();
w += self.margin;
h += self.margin;
if let Some((i, mut rect)) = self.find_skyline(w, h) {
if w == rect.w {
self.buf.patch(rect.x, rect.y, buf);
} else {
self.buf.patch_rotated(rect.x, rect.y, buf);
}
self.split(i, &rect);
self.merge();
rect.w -= self.margin;
rect.h -= self.margin;
Some(rect)
} else {
None
}
}
fn buf(&self) -> &B {
&self.buf
}
fn buf_mut(&mut self) -> &mut B {
&mut self.buf
}
fn into_buf(self) -> B {
self.buf
}
}

Loading…
Cancel
Save