Newer
Older
use image::{GenericImage, GenericImageView, Pixel, Rgba, RgbaImage};
use num_traits::cast::ToPrimitive;
#[inline(always)]
fn tile_size() -> u32 {
32
}
/// Take each tile in an image and expand its borders by a given amount. Optionally fill with
/// nearby pixels instead of empty space
#[derive(Parser, Serialize, Deserialize, Clone, Debug)]
#[clap(author, version = "0.3.0")]
pub struct Extrude {
/// The path to the spritesheet file
#[serde(default)]
pub input: String,
/// The path to write the extruded spritesheet
#[serde(default)]
pub output: String,
/// The amount of horizontal space to add between each sprite in the image
#[clap(short = 'x', long, default_value_t = 0)]
/// The amount of vertical space to add between each sprite in the image
#[clap(short = 'y', long, default_value_t = 0)]
/// The amount of padding to add around the edge of the spritesheet
#[clap(short, long, default_value_t = 0)]
/// The size of each tile in the spritesheet. Assumed to be square tiles
#[clap(short, long, default_value_t = 32)]
#[serde(default = "tile_size")]
tile_size: u32,
/// Use nearby pixels for spacing
#[clap(short, long)]
#[serde(default)]
extrude: bool,
}
impl Extrude {
pub fn run(&self, image: &impl GenericImage) -> anyhow::Result<RgbaOutputFormat> {
log::info!(
"Image loaded with size {} x {}",
image.width(),
image.height()
);
let columns = image.width() / self.tile_size;
let rows = image.height() / self.tile_size;
log::info!("Inferred sheet contains {} columns", columns);
log::info!("Inferred sheet contains {} rows", rows);
let mut views = Vec::with_capacity((columns * rows) as usize);
for x in 0..columns {
for y in 0..rows {
let img_x = x * self.tile_size;
let img_y = y * self.tile_size;
let payload = SpriteData {
data: image.view(img_x, img_y, self.tile_size, self.tile_size),
x: img_x,
y: img_y,
tx: x,
ty: y,
width: self.tile_size,
height: self.tile_size,
};
let new_width = (self.padding * 2 + self.space_x * columns) + image.width();
let new_height = (self.padding * 2 + self.space_y * rows) + image.height();
log::info!(
"Using new image width {} / height {}",
new_width,
new_height
);
let mut new_image = RgbaImage::new(new_width, new_height);
let (new_image_x, new_image_y, new_image_width, new_image_height) = new_image.bounds();
for x in new_image_x..new_image_width {
for y in new_image_y..new_image_height {
new_image.put_pixel(x, y, Rgba::from([0, 0, 0, 0]));
}
for sprite in views.iter() {
let (img_x, img_y, width, height) = sprite.data.bounds();
for x in 0..width {
for y in 0..height {
let pix = sprite.data.get_pixel(x, y).to_rgba();
let p = Rgba::from([
pix.0[0].to_u8().unwrap(),
pix.0[1].to_u8().unwrap(),
pix.0[2].to_u8().unwrap(),
pix.0[3].to_u8().unwrap(),
]);
self.padding + (sprite.tx * self.space_x) + img_x + x,
self.padding + (sprite.ty * self.space_y) + img_y + y,