use std::collections::HashMap;
use {
TexturePackerConfig,
};
use rect::Rect;
use texture::{
Pixel,
Texture,
SubTexture,
};
use frame::Frame;
use packer::{
Packer,
SkylinePacker,
};
pub struct TexturePacker<'a, T: 'a + Clone, P> {
textures: HashMap<String, SubTexture<'a, T>>,
frames: HashMap<String, Frame>,
packer: P,
config: TexturePackerConfig,
}
impl <'a, Pix: Pixel, T: 'a + Clone + Texture<Pixel=Pix>> TexturePacker<'a, T, SkylinePacker<Pix>> {
pub fn new_skyline(config: TexturePackerConfig) -> TexturePacker<'a, T, SkylinePacker<Pix>> {
TexturePacker {
textures: HashMap::new(),
frames: HashMap::new(),
packer: SkylinePacker::<Pix>::new(config),
config: config,
}
}
}
impl<'a, Pix: Pixel, P: Packer<Pixel=Pix>, T: Clone + Texture<Pixel=Pix>> TexturePacker<'a, T, P> {
pub fn pack_ref(&mut self, key: String, texture: &'a T) {
let (w, h) = (texture.width(), texture.height());
let source = if self.config.trim {
trim_texture(texture)
} else {
Rect::new(0, 0, w, h)
};
let texture = SubTexture::from_ref(texture, source);
if let Some(mut frame) = self.packer.pack(key.clone(), &texture) {
frame.frame.x += self.config.border_padding;
frame.frame.y += self.config.border_padding;
frame.trimmed = self.config.trim;
frame.source = source;
frame.source.w = w;
frame.source.h = h;
self.frames.insert(key.clone(), frame);
}
self.textures.insert(key, texture);
}
pub fn pack_own(&mut self, key: String, texture: T) {
let (w, h) = (texture.width(), texture.height());
let source = if self.config.trim {
trim_texture(&texture)
} else {
Rect::new(0, 0, w, h)
};
let texture = SubTexture::new(texture, source);
if let Some(mut frame) = self.packer.pack(key.clone(), &texture) {
frame.frame.x += self.config.border_padding;
frame.frame.y += self.config.border_padding;
frame.trimmed = self.config.trim;
frame.source = source;
frame.source.w = w;
frame.source.h = h;
self.frames.insert(key.clone(), frame);
}
self.textures.insert(key, texture);
}
pub fn get_frames(&self) -> &HashMap<String, Frame> {
&self.frames
}
pub fn get_frame(&self, key: &str) -> Option<&Frame> {
if let Some(frame) = self.frames.get(key) {
Some(frame)
} else {
None
}
}
fn get_frame_at(&self, x: u32, y: u32) -> Option<&Frame> {
for (_, frame) in self.frames.iter() {
if frame.frame.contains_point(x, y) {
return Some(frame);
}
}
None
}
}
impl<'a, Pix, P, T: Clone> Texture for TexturePacker<'a, T, P>
where Pix: Pixel, P: Packer<Pixel=Pix>, T: Texture<Pixel=Pix> {
type Pixel = Pix;
fn width(&self) -> u32 {
let mut right = None;
for (_, frame) in self.frames.iter() {
if let Some(r) = right {
if frame.frame.right() > r {
right = Some(frame.frame.right());
}
} else {
right = Some(frame.frame.right());
}
}
if let Some(right) = right {
right + 1 + self.config.border_padding
} else {
0
}
}
fn height(&self) -> u32 {
let mut bottom = None;
for (_, frame) in self.frames.iter() {
if let Some(b) = bottom {
if frame.frame.bottom() > b {
bottom = Some(frame.frame.bottom());
}
} else {
bottom = Some(frame.frame.bottom());
}
}
if let Some(bottom) = bottom {
bottom + 1 + self.config.border_padding
} else {
0
}
}
fn get(&self, x: u32, y: u32) -> Option<Pix> {
if let Some(frame) = self.get_frame_at(x, y) {
if let Some(texture) = self.textures.get(&frame.key) {
if self.config.texture_outlines && frame.frame.is_outline(x, y) {
return Some(<Pix as Pixel>::outline());
}
let x = x - frame.frame.x;
let y = y - frame.frame.y;
return if frame.rotated {
texture.get_rotated(x, y)
} else {
texture.get(x, y)
};
}
}
None
}
fn set(&mut self, _x: u32, _y: u32, _val: Pix) {
panic!("Can't set pixel directly");
}
}
fn trim_texture<T: Texture>(texture: &T) -> Rect {
let mut x1 = 0;
for x in 0..texture.width() {
if texture.is_column_transparent(x) {
x1 = x + 1;
} else {
break;
}
}
let mut x2 = texture.width() - 1;
for x in 0..texture.width() {
let x = texture.width() - x - 1;
if texture.is_column_transparent(x) {
x2 = x - 1;
} else {
break;
}
}
let mut y1 = 0;
for y in 0..texture.height() {
if texture.is_row_transparent(y) {
y1 = y + 1;
} else {
break;
}
}
let mut y2 = texture.height() - 1;
for y in 0..texture.height() {
let y = texture.height() - y - 1;
if texture.is_row_transparent(y) {
y2 = y - 1;
} else {
break;
}
}
Rect::new_with_points(x1, y1, x2, y2)
}