use image::{Rgba, RgbaImage};
const TRANSPARENT: Rgba<u8> = Rgba([0, 0, 0, 0]);
pub fn render_spritesheet(frames: &[RgbaImage], cols: Option<u32>) -> RgbaImage {
if frames.is_empty() {
return RgbaImage::from_pixel(1, 1, TRANSPARENT);
}
let max_width = frames.iter().map(|f| f.width()).max().unwrap_or(1);
let max_height = frames.iter().map(|f| f.height()).max().unwrap_or(1);
let num_frames = frames.len() as u32;
let columns = cols.unwrap_or(num_frames); let rows = num_frames.div_ceil(columns);
let sheet_width = columns * max_width;
let sheet_height = rows * max_height;
let mut sheet = RgbaImage::from_pixel(sheet_width, sheet_height, TRANSPARENT);
for (i, frame) in frames.iter().enumerate() {
let col = (i as u32) % columns;
let row = (i as u32) / columns;
let dest_x = col * max_width;
let dest_y = row * max_height;
for y in 0..frame.height() {
for x in 0..frame.width() {
let pixel = *frame.get_pixel(x, y);
sheet.put_pixel(dest_x + x, dest_y + y, pixel);
}
}
}
sheet
}
#[cfg(test)]
mod tests {
use super::*;
fn make_solid_frame(width: u32, height: u32, color: Rgba<u8>) -> RgbaImage {
RgbaImage::from_pixel(width, height, color)
}
#[test]
fn test_empty_frames() {
let sheet = render_spritesheet(&[], None);
assert_eq!(sheet.width(), 1);
assert_eq!(sheet.height(), 1);
assert_eq!(*sheet.get_pixel(0, 0), TRANSPARENT);
}
#[test]
fn test_single_frame() {
let red = Rgba([255, 0, 0, 255]);
let frame = make_solid_frame(3, 3, red);
let sheet = render_spritesheet(&[frame], None);
assert_eq!(sheet.width(), 3);
assert_eq!(sheet.height(), 3);
assert_eq!(*sheet.get_pixel(0, 0), red);
assert_eq!(*sheet.get_pixel(2, 2), red);
}
#[test]
fn test_four_frames_horizontal() {
let red = Rgba([255, 0, 0, 255]);
let green = Rgba([0, 255, 0, 255]);
let blue = Rgba([0, 0, 255, 255]);
let yellow = Rgba([255, 255, 0, 255]);
let frames = vec![
make_solid_frame(2, 2, red),
make_solid_frame(2, 2, green),
make_solid_frame(2, 2, blue),
make_solid_frame(2, 2, yellow),
];
let sheet = render_spritesheet(&frames, None);
assert_eq!(sheet.width(), 8); assert_eq!(sheet.height(), 2);
assert_eq!(*sheet.get_pixel(0, 0), red); assert_eq!(*sheet.get_pixel(2, 0), green); assert_eq!(*sheet.get_pixel(4, 0), blue); assert_eq!(*sheet.get_pixel(6, 0), yellow); }
#[test]
fn test_different_sized_frames_padded() {
let red = Rgba([255, 0, 0, 255]);
let green = Rgba([0, 255, 0, 255]);
let small_frame = make_solid_frame(2, 2, red);
let large_frame = make_solid_frame(4, 4, green);
let frames = vec![small_frame, large_frame];
let sheet = render_spritesheet(&frames, None);
assert_eq!(sheet.width(), 8);
assert_eq!(sheet.height(), 4);
assert_eq!(*sheet.get_pixel(0, 0), red);
assert_eq!(*sheet.get_pixel(1, 1), red);
assert_eq!(*sheet.get_pixel(2, 0), TRANSPARENT); assert_eq!(*sheet.get_pixel(0, 2), TRANSPARENT);
assert_eq!(*sheet.get_pixel(4, 0), green);
assert_eq!(*sheet.get_pixel(7, 3), green);
}
#[test]
fn test_custom_columns_2x2() {
let red = Rgba([255, 0, 0, 255]);
let green = Rgba([0, 255, 0, 255]);
let blue = Rgba([0, 0, 255, 255]);
let yellow = Rgba([255, 255, 0, 255]);
let frames = vec![
make_solid_frame(2, 2, red),
make_solid_frame(2, 2, green),
make_solid_frame(2, 2, blue),
make_solid_frame(2, 2, yellow),
];
let sheet = render_spritesheet(&frames, Some(2));
assert_eq!(sheet.width(), 4); assert_eq!(sheet.height(), 4);
assert_eq!(*sheet.get_pixel(0, 0), red);
assert_eq!(*sheet.get_pixel(2, 0), green);
assert_eq!(*sheet.get_pixel(0, 2), blue);
assert_eq!(*sheet.get_pixel(2, 2), yellow);
}
#[test]
fn test_uneven_grid() {
let red = Rgba([255, 0, 0, 255]);
let green = Rgba([0, 255, 0, 255]);
let blue = Rgba([0, 0, 255, 255]);
let frames = vec![
make_solid_frame(2, 2, red),
make_solid_frame(2, 2, green),
make_solid_frame(2, 2, blue),
];
let sheet = render_spritesheet(&frames, Some(2));
assert_eq!(sheet.width(), 4); assert_eq!(sheet.height(), 4);
assert_eq!(*sheet.get_pixel(0, 0), red);
assert_eq!(*sheet.get_pixel(2, 0), green);
assert_eq!(*sheet.get_pixel(0, 2), blue);
assert_eq!(*sheet.get_pixel(2, 2), TRANSPARENT);
}
#[test]
fn test_cols_greater_than_frames() {
let red = Rgba([255, 0, 0, 255]);
let frames = vec![
make_solid_frame(2, 2, red),
make_solid_frame(2, 2, red),
make_solid_frame(2, 2, red),
];
let sheet = render_spritesheet(&frames, Some(10));
assert_eq!(sheet.width(), 20);
assert_eq!(sheet.height(), 2);
assert_eq!(*sheet.get_pixel(0, 0), red);
assert_eq!(*sheet.get_pixel(2, 0), red);
assert_eq!(*sheet.get_pixel(4, 0), red);
assert_eq!(*sheet.get_pixel(6, 0), TRANSPARENT);
}
#[test]
fn test_single_column() {
let red = Rgba([255, 0, 0, 255]);
let green = Rgba([0, 255, 0, 255]);
let blue = Rgba([0, 0, 255, 255]);
let frames = vec![
make_solid_frame(3, 2, red),
make_solid_frame(3, 2, green),
make_solid_frame(3, 2, blue),
];
let sheet = render_spritesheet(&frames, Some(1));
assert_eq!(sheet.width(), 3); assert_eq!(sheet.height(), 6);
assert_eq!(*sheet.get_pixel(0, 0), red);
assert_eq!(*sheet.get_pixel(0, 2), green);
assert_eq!(*sheet.get_pixel(0, 4), blue);
}
}