Skip to content
Snippets Groups Projects
cli_args.rs 4.95 KiB
Newer Older
Louis's avatar
Louis committed
use clap::{Parser, Subcommand};
use serde::{Deserialize, Serialize};

use crate::commands::{
	calculate_mapping, execute_pipeline, extrude, flip, palette, remap_image, rescale, rotate,
	write_palette, FlipDirection, RotateDegree,
};
use crate::format::PaletteFormat;
use crate::{load_image, Format};

/// Image utilities for Advent
#[derive(Parser, Debug, Clone)]
#[clap(name = "Crunch")]
#[clap(author = "Louis Capitanchik <louis@microhacks.co.uk>")]
Louis's avatar
Louis committed
#[clap(version = "0.4.0")]
Louis's avatar
Louis committed
#[clap(about, long_about = None)]
pub struct Args {
	/// The path to the spritesheet file
	in_path: String,
	/// The path to the output file
	out_path: String,

	/// Force Crunch to read the input file as a specific format
	#[clap(short, long, arg_enum)]
	format: Option<Format>,

	#[clap(subcommand)]
	command: CrunchCommand,
}

#[inline]
pub fn u32_zero() -> u32 {
	0
}
#[inline]
pub fn u32_32() -> u32 {
	32
}
#[inline]
pub fn f32_one() -> f32 {
	1.0
}

#[derive(Debug, Clone, Subcommand, Serialize, Deserialize)]
#[serde(tag = "command", content = "params")]
pub enum CrunchCommand {
	/// Take each tile in an image and expand its borders by a given amount
	#[clap(version = "0.3.0")]
	Extrude {
		/// The amount of horizontal padding to add between each sprite in the image
		#[clap(long, default_value_t = 0)]
		#[serde(default = "u32_zero")]
		space_x: u32,
		/// The amount of vertical padding to add between each sprite in the image
		#[clap(long, default_value_t = 0)]
		#[serde(default = "u32_zero")]
		space_y: u32,
		/// The amount of horizontal padding to add between each sprite in the image
		#[clap(long, default_value_t = 0)]
		#[serde(default = "u32_zero")]
		pad_x: u32,
		/// The amount of vertical padding to add between each sprite in the image
		#[clap(long, default_value_t = 0)]
		#[serde(default = "u32_zero")]
		pad_y: u32,
		/// The size of each tile in the spritesheet. Assumed to be square tiles
		#[clap(short, long, default_value_t = 32)]
		#[serde(default = "u32_32")]
		tile_size: u32,
		/// Use nearby pixels for padding
		#[clap(short, long)]
		#[serde(default)]
		extrude: bool,
	},
	/// Create a palette file containing every distinct colour from the input image
	#[clap(version = "0.3.0")]
	Palette {
		#[clap(short, long, arg_enum, default_value_t)]
		#[serde(default)]
		format: PaletteFormat,
	},
	/// Convert the colour space of an input image to the given palette
	#[clap(version = "0.3.0")]
	Remap {
		/// The path to the palette file containing the output colours
		palette_file: String,
	},
	/// Make an image larger or smaller
	#[clap(version = "0.3.0")]
	Scale {
		/// The scale factor to use; numbers between 0-1 shrink the image; numbers > 1 enlarge
		#[clap(long, default_value_t = 1.0)]
		#[serde(default = "f32_one")]
		factor: f32,
	},
	/// Apply a clockwise rotation to the image
	#[clap(version = "0.3.0")]
	Rotate {
		/// How many 90 degree steps should this image be rotated by
		#[clap(long, arg_enum)]
		amount: RotateDegree,
	},
	/// Flip an image along one or both axis
	#[clap(version = "0.3.0")]
	Flip {
		/// The axis along which the image should be flipped
		#[clap(long, arg_enum)]
		direction: FlipDirection,
	},
	/// Execute a pipeline file to run multiple commands on one or more images
	#[clap(version = "0.3.0")]
	Pipeline,
}

impl Args {
	pub fn run(&self) -> anyhow::Result<()> {
		match &self.command {
			CrunchCommand::Extrude {
				extrude: ext,
				pad_x,
				pad_y,
				space_x,
				space_y,
				tile_size,
			} => {
				let image = load_image(&self.in_path, self.format)?;
				let output = extrude(image, *tile_size, *pad_x, *pad_y, *space_x, *space_y, *ext)?;
				output.save(&self.out_path).map_err(anyhow::Error::from)
			}
			CrunchCommand::Palette { format } => {
				let image = load_image(&self.in_path, self.format)?;
				let output = palette(&image)?;
				write_palette(output, *format, &self.out_path)
			}
			CrunchCommand::Remap { palette_file } => {
				let image_data = load_image(&self.in_path, self.format)?;
				let palette_data = load_image(&palette_file, self.format)?;

				let image_palette = palette(&image_data)?;
				let target_palette = palette(&palette_data)?;

				let mappings = calculate_mapping(&image_palette, &target_palette);
				let output = remap_image(image_data, mappings)?;

				output.save(&self.out_path).map_err(anyhow::Error::from)
			}
			CrunchCommand::Scale { factor } => {
				let image = load_image(self.in_path.clone(), self.format)?;
				let output = rescale(&image, *factor)?;
				output.save(&self.out_path).map_err(anyhow::Error::from)
			}
			CrunchCommand::Rotate { amount } => {
				let image = load_image(self.in_path.clone(), self.format)?;
				let output = rotate(&image, *amount)?;
				output.save(&self.out_path).map_err(anyhow::Error::from)
			}
			CrunchCommand::Flip { direction } => {
				let image = load_image(self.in_path.clone(), self.format)?;
				let output = flip(&image, *direction)?;
				output.save(&self.out_path).map_err(anyhow::Error::from)
			}
			CrunchCommand::Pipeline => execute_pipeline(&self.in_path, &self.out_path),
		}
	}
}