Skip to content
Snippets Groups Projects
Verified Commit 1f1ff9ef authored by Louis's avatar Louis :fire:
Browse files

Add glob support to pipelines

parent bba43b0a
No related branches found
No related tags found
No related merge requests found
......@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.4.0] - Unreleased
### Added
- Support for GlobRef definitions in a `pipeline.toml` file
- Takes a `pattern` glob expression instead of an `input_path`
- Takes a directory path as `output_dir` instead of a file path as `output_file`, and will construct the an `output_file` by appending the file name of a matched file to the output directory
## [0.3.0] - 2022-07-08
### Added
- Added `flip` command, for flipping an image along one or both axes.
......
......@@ -183,13 +183,14 @@ dependencies = [
[[package]]
name = "crunch"
version = "0.3.0"
version = "0.4.0"
dependencies = [
"anyhow",
"clap",
"deltae",
"env_logger",
"glam",
"glob",
"image",
"lab",
"log",
......@@ -315,6 +316,12 @@ version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f43e957e744be03f5801a55472f593d43fabdebf25a4585db250f04d86b1675f"
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "half"
version = "1.8.2"
......
[package]
name = "crunch"
version = "0.3.0"
version = "0.4.0"
edition = "2021"
[dependencies]
......@@ -18,3 +18,4 @@ thiserror = "1.0.30"
serde = { version = "1.0.131", features = ["derive"] }
toml = "0.5.9"
serde_json = "1.0.81"
glob = "0.3.0"
hard_tabs = true
group_imports = "StdExternalCrate"
#group_imports = "StdExternalCrate"
use_field_init_shorthand = true
use_try_shorthand = true
\ No newline at end of file
......@@ -12,7 +12,7 @@ use crate::{load_image, Format};
#[derive(Parser, Debug, Clone)]
#[clap(name = "Crunch")]
#[clap(author = "Louis Capitanchik <louis@microhacks.co.uk>")]
#[clap(version = "0.3.0")]
#[clap(version = "0.4.0")]
#[clap(about, long_about = None)]
pub struct Args {
/// The path to the spritesheet file
......
use glob::{glob_with, MatchOptions};
use std::collections::HashMap;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
......@@ -27,6 +28,11 @@ pub enum PipelineType {
output_path: String,
reference: String,
},
GlobRef {
pattern: String,
output_dir: String,
reference: String,
},
}
#[derive(Clone, Debug, Serialize, Deserialize)]
......@@ -56,15 +62,165 @@ pub fn execute_pipeline<IN: ToString, OUT: ToString>(
serde_json::from_slice(&file_contents)?
};
get_targets(&pipeline_data).for_each(|(input_path, output_path, actions)| {
let mut file = match load_image(&input_path, None) {
Ok(image) => image,
Err(e) => {
log::error!("Error loading {}; {:?}", &input_path, e);
return;
}
};
let mut count = 1;
for step in actions {
match step {
CrunchCommand::Extrude {
tile_size,
space_y,
space_x,
pad_y,
pad_x,
extrude,
} => {
file = match commands::extrude(
file, tile_size, pad_x, pad_y, space_x, space_y, extrude,
) {
Ok(f) => f,
Err(e) => {
log::error!(
"Failed to extrude {} at step {}; {}",
input_path,
count,
e
);
return;
}
};
}
CrunchCommand::Remap { palette_file } => {
let palette_data = match load_image(&palette_file, None) {
Ok(p) => p,
Err(e) => {
log::error!("Failed to load {} at step {}; {:?}", input_path, count, e);
return;
}
};
let image_palette = match commands::palette(&file) {
Ok(ip) => ip,
Err(e) => {
log::error!(
"Failed to extract palette from {} at step {}; {}",
input_path,
count,
e
);
return;
}
};
let target_palette = match commands::palette(&palette_data) {
Ok(tp) => tp,
Err(e) => {
log::error!(
"Failed to extract palette from {} at step {}; {}",
&palette_file,
count,
e
);
return;
}
};
let mappings = commands::calculate_mapping(&image_palette, &target_palette);
file = match commands::remap_image(file, mappings) {
Ok(f) => f,
Err(e) => {
log::error!("Failed to remap {} at step {}; {}", input_path, count, e);
return;
}
};
}
CrunchCommand::Scale { factor } => {
file = match commands::rescale(&file, factor) {
Ok(f) => f,
Err(e) => {
log::error!("Failed to scale {} at step {}; {}", input_path, count, e);
return;
}
};
}
CrunchCommand::Rotate { amount } => {
file = match commands::rotate(&file, amount) {
Ok(f) => f,
Err(e) => {
log::error!(
"Failed to rotate {} by {:?} step(s); {}",
input_path,
amount,
e
);
return;
}
};
}
CrunchCommand::Flip { direction } => {
file = match commands::flip(&file, direction) {
Ok(f) => f,
Err(e) => {
log::error!(
"Failed to flip {} in the following direction: {:?}; {}",
input_path,
direction,
e
);
return;
}
};
}
CrunchCommand::Palette { .. } | CrunchCommand::Pipeline => continue,
}
count += 1;
}
let mut outer_target_path = PathBuf::from(&output_path);
outer_target_path.pop();
if let Err(e) = std::fs::create_dir(&outer_target_path) {
match e.kind() {
std::io::ErrorKind::AlreadyExists => { /* This is fine */ }
_ => log::error!(
"Failed to create containing directory {}; {}",
outer_target_path.to_string_lossy(),
e
),
}
}
match file.save(&output_path) {
Ok(_) => {}
Err(e) => {
log::error!("Failed to save to {}; {}", output_path, e);
}
}
});
Ok(())
}
fn get_targets(
pipeline_data: &PipelineFile,
) -> impl ParallelIterator<Item = (String, String, Vec<CrunchCommand>)> + '_ {
pipeline_data
.pipelines
.par_iter()
.filter_map(|pipe| match pipe {
.flat_map(|pipe| match pipe {
PipelineType::Pipeline {
input_path,
output_path,
actions,
} => Some((input_path, output_path, actions.clone())),
} => vec![(input_path.clone(), output_path.clone(), actions.clone())],
PipelineType::Ref {
input_path,
output_path,
......@@ -72,166 +228,54 @@ pub fn execute_pipeline<IN: ToString, OUT: ToString>(
} => pipeline_data
.refs
.get(reference.as_str())
.map(|value| (input_path, output_path, value.actions.clone())),
})
.for_each(|(input_path, output_path, actions)| {
let mut file = match load_image(input_path, None) {
Ok(image) => image,
Err(e) => {
log::error!("Error loading {}; {:?}", input_path, e);
return;
}
};
let mut count = 1;
for step in actions {
match step {
CrunchCommand::Extrude {
tile_size,
space_y,
space_x,
pad_y,
pad_x,
extrude,
} => {
file = match commands::extrude(
file, tile_size, pad_x, pad_y, space_x, space_y, extrude,
) {
Ok(f) => f,
Err(e) => {
log::error!(
"Failed to extrude {} at step {}; {}",
input_path,
count,
e
);
return;
}
};
}
CrunchCommand::Remap { palette_file } => {
let palette_data = match load_image(&palette_file, None) {
Ok(p) => p,
Err(e) => {
log::error!(
"Failed to load {} at step {}; {:?}",
input_path,
count,
e
);
return;
}
};
let image_palette = match commands::palette(&file) {
Ok(ip) => ip,
Err(e) => {
log::error!(
"Failed to extract palette from {} at step {}; {}",
input_path,
count,
e
);
return;
}
};
let target_palette = match commands::palette(&palette_data) {
Ok(tp) => tp,
Err(e) => {
log::error!(
"Failed to extract palette from {} at step {}; {}",
&palette_file,
count,
e
);
return;
}
};
let mappings = commands::calculate_mapping(&image_palette, &target_palette);
file = match commands::remap_image(file, mappings) {
Ok(f) => f,
Err(e) => {
log::error!(
"Failed to remap {} at step {}; {}",
input_path,
count,
e
);
return;
}
};
}
CrunchCommand::Scale { factor } => {
file = match commands::rescale(&file, factor) {
Ok(f) => f,
Err(e) => {
log::error!(
"Failed to scale {} at step {}; {}",
input_path,
count,
e
);
return;
}
};
}
CrunchCommand::Rotate { amount } => {
file = match commands::rotate(&file, amount) {
Ok(f) => f,
Err(e) => {
log::error!(
"Failed to rotate {} by {:?} step(s); {}",
input_path,
amount,
e
);
return;
}
};
}
CrunchCommand::Flip { direction } => {
file = match commands::flip(&file, direction) {
Ok(f) => f,
Err(e) => {
log::error!(
"Failed to flip {} in the following direction: {:?}; {}",
input_path,
direction,
e
);
return;
}
};
.iter()
.map(|value| {
(
input_path.clone(),
output_path.clone(),
(*value).actions.clone(),
)
})
.collect(),
PipelineType::GlobRef {
pattern,
output_dir,
reference,
} => pipeline_data
.refs
.get(reference.as_str())
.iter()
.map(|value| (*value).actions.clone())
.flat_map(|actions| {
let mut paths = Vec::new();
for entry in glob_with(
pattern.as_str(),
MatchOptions {
case_sensitive: true,
..Default::default()
},
)
.unwrap()
{
paths.push((actions.clone(), entry));
}
CrunchCommand::Palette { .. } | CrunchCommand::Pipeline => continue,
}
paths
})
.filter_map(|(actions, inner)| inner.ok().map(|p| (actions, p)))
.filter_map(|(actions, path)| {
if let Some(filename) = path.file_name().and_then(|osstr| osstr.to_str()) {
let output_path = Path::new(output_dir.as_str());
let output_path = output_path.join(filename);
count += 1;
}
let mut outer_target_path = PathBuf::from(output_path);
outer_target_path.pop();
if let Err(e) = std::fs::create_dir(&outer_target_path) {
match e.kind() {
std::io::ErrorKind::AlreadyExists => { /* This is fine */ }
_ => log::error!(
"Failed to create containing directory {}; {}",
outer_target_path.to_string_lossy(),
e
),
}
}
match file.save(output_path) {
Ok(_) => {}
Err(e) => {
log::error!("Failed to save to {}; {}", output_path, e);
}
}
});
Ok(())
Some((
format!("{}", path.display()),
format!("{}", output_path.display()),
actions,
))
} else {
None
}
})
.collect(),
})
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment