Skip to content
Snippets Groups Projects
Commit 8488fc84 authored by Louis's avatar Louis :fire:
Browse files

Add palette swap command

parent e5557d3f
No related branches found
No related tags found
No related merge requests found
Pipeline #675 waiting for manual action with stages
in 1 minute and 41 seconds
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]] [[package]]
name = "adler" name = "adler"
...@@ -233,7 +233,7 @@ dependencies = [ ...@@ -233,7 +233,7 @@ dependencies = [
[[package]] [[package]]
name = "crunch-cli" name = "crunch-cli"
version = "0.8.2" version = "0.9.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
......
[package] [package]
name = "crunch-cli" name = "crunch-cli"
version = "0.8.2" version = "0.9.0"
edition = "2021" edition = "2021"
homepage = "https://microhacks.lcr.app/crunch/" homepage = "https://microhacks.lcr.app/crunch/"
......
...@@ -2,9 +2,7 @@ use clap::Parser; ...@@ -2,9 +2,7 @@ use clap::Parser;
use image::ImageFormat; use image::ImageFormat;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::commands::{ use crate::commands::{Atlas, Extract, Extrude, Flip, Info, Palette, Pipeline, Reduce, Remap, Rotate, Scale, Split, Swap};
Atlas, Extract, Extrude, Flip, Info, Palette, Pipeline, Reduce, Remap, Rotate, Scale, Split,
};
use crate::load_image; use crate::load_image;
...@@ -53,6 +51,9 @@ pub enum Args { ...@@ -53,6 +51,9 @@ pub enum Args {
#[clap(name = "info")] #[clap(name = "info")]
#[serde(alias = "info")] #[serde(alias = "info")]
Info(Info), Info(Info),
#[clap(name = "info")]
#[serde(alias = "info")]
Swap(Swap),
} }
impl Args { impl Args {
...@@ -104,6 +105,15 @@ impl Args { ...@@ -104,6 +105,15 @@ impl Args {
.save_with_format(&remap.output, ImageFormat::Png) .save_with_format(&remap.output, ImageFormat::Png)
.map_err(anyhow::Error::from) .map_err(anyhow::Error::from)
} }
Args::Swap(swap) => {
let image_data = load_image(&swap.input, None)?;
let palette_data = load_image(&swap.palette, None)?;
let palette_swap = Palette::from_columns(&palette_data)?;
let output = Remap::remap_image(image_data, palette_swap)?;
output
.save_with_format(&swap.output, ImageFormat::Png)
.map_err(anyhow::Error::from)
},
Args::Reduce(reduce) => { Args::Reduce(reduce) => {
if let Some(amount) = reduce.colours { if let Some(amount) = reduce.colours {
log::info!("Num cols {}", amount); log::info!("Num cols {}", amount);
......
...@@ -10,6 +10,7 @@ mod remap; ...@@ -10,6 +10,7 @@ mod remap;
mod rotate; mod rotate;
mod scale; mod scale;
mod split; mod split;
mod swap;
pub use atlas::Atlas; pub use atlas::Atlas;
pub use extract::Extract; pub use extract::Extract;
...@@ -23,3 +24,4 @@ pub use remap::Remap; ...@@ -23,3 +24,4 @@ pub use remap::Remap;
pub use rotate::Rotate; pub use rotate::Rotate;
pub use scale::Scale; pub use scale::Scale;
pub use split::Split; pub use split::Split;
pub use swap::Swap;
\ No newline at end of file
...@@ -4,7 +4,7 @@ use std::collections::hash_map::RandomState; ...@@ -4,7 +4,7 @@ use std::collections::hash_map::RandomState;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::io::Write; use std::io::Write;
use anyhow::Error;
use deltae::{Delta, LabValue, DE2000}; use deltae::{Delta, LabValue, DE2000};
use image::{GenericImage, Pixel, Rgba}; use image::{GenericImage, Pixel, Rgba};
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
...@@ -110,6 +110,37 @@ impl Palette { ...@@ -110,6 +110,37 @@ impl Palette {
Ok(()) Ok(())
} }
pub fn from_columns(image: &impl GenericImage) -> anyhow::Result<ColourMapping> {
if image.width() != 2 {
return Err(Error::msg("Image must have a pixel width of 2"))?;
}
let mut mapping = ColourMapping::with_capacity(image.height() as usize);
for y in 0..image.height() {
let left = image.get_pixel(0, y);
let right = image.get_pixel(1, y);
let left = left.to_rgba();
let right = right.to_rgba();
let data = Rgba::from([
left.0[0].to_u8().unwrap(),
left.0[1].to_u8().unwrap(),
left.0[2].to_u8().unwrap(),
left.0[3].to_u8().unwrap(),
]);
let left_pixel = BasicRgba::from(data);
let data = Rgba::from([
left.0[0].to_u8().unwrap(),
left.0[1].to_u8().unwrap(),
left.0[2].to_u8().unwrap(),
left.0[3].to_u8().unwrap(),
]);
let right_pixel = BasicRgba::from(data);
mapping.insert(left_pixel, right_pixel);
}
Ok(mapping)
}
pub fn calculate_mapping(from: &PixelPalette, to: &PixelPalette) -> ColourMapping { pub fn calculate_mapping(from: &PixelPalette, to: &PixelPalette) -> ColourMapping {
let colour_labs = Vec::from_iter(to.iter().map(LabValue::from)); let colour_labs = Vec::from_iter(to.iter().map(LabValue::from));
......
use crate::commands::palette::ColourMapping;
use clap::Parser;
use image::{GenericImage, Pixel, Rgba};
use num_traits::ToPrimitive;
use serde::{Deserialize, Serialize};
use crate::utils::{new_image, BasicRgba, OutputFormat};
/// Use an existing colour mapping file to swap colours in the input sprite
#[derive(Debug, Clone, Parser, Serialize, Deserialize)]
#[clap(author, version = "0.9.0")]
pub struct Swap {
/// The path to the image file
#[serde(default)]
pub input: String,
/// The path to write the recoloured image
#[serde(default)]
pub output: String,
/// The path to the palette file containing the output colours
#[clap(short, long)]
pub palette: String,
}
impl Swap {
pub fn swap_pixels(
image: impl GenericImage,
mappings: ColourMapping,
) -> anyhow::Result<OutputFormat> {
let mut output = new_image(image.width(), image.height());
for (x, y, pixel) in image.pixels() {
let pixel = pixel.to_rgba();
if pixel.0[3].to_u8().unwrap() == 0 {
output.put_pixel(x, y, Rgba::from(BasicRgba::transparent()));
continue;
}
let data = Rgba::from([
pixel.0[0].to_u8().unwrap(),
pixel.0[1].to_u8().unwrap(),
pixel.0[2].to_u8().unwrap(),
pixel.0[3].to_u8().unwrap(),
]);
let basic = BasicRgba::from(data);
match mappings.get(&basic) {
Some(mapped_data) => output.put_pixel(
x,
y,
Rgba::from(BasicRgba {
a: basic.a,
..*mapped_data
}),
),
None => output.put_pixel(x, y, data),
};
}
Ok(output)
}
}
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