recolor outlines

This commit is contained in:
2025-12-19 20:08:34 +08:00
parent dfc826b093
commit 3228394c64

View File

@@ -7,25 +7,40 @@ use std::process;
fn main() { fn main() {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
if args.len() < 4 { if args.len() < 5 {
print_usage(&args[0]); print_usage(&args[0]);
process::exit(1); process::exit(1);
} }
let input_path = &args[1]; let input_path = &args[1];
let output_path = &args[2]; let output_path = &args[2];
let color_input = &args[3]; let white_color_input = &args[3];
let apply_texture = args.len() > 4 && args[4].to_lowercase() == "true"; let black_color_input = &args[4];
let apply_texture = args.len() > 5 && args[5].to_lowercase() == "true";
let target_color = match parse_hex_color(color_input) { let white_color = match parse_hex_color(white_color_input) {
Ok(color) => color, Ok(color) => color,
Err(e) => { Err(e) => {
eprintln!("Error: {}", e); eprintln!("Error parsing white color: {}", e);
process::exit(1); process::exit(1);
} }
}; };
match recolor_gif(input_path, output_path, target_color, apply_texture) { let black_color = match parse_hex_color(black_color_input) {
Ok(color) => color,
Err(e) => {
eprintln!("Error parsing black color: {}", e);
process::exit(1);
}
};
match recolor_gif(
input_path,
output_path,
white_color,
black_color,
apply_texture,
) {
Ok(_) => (), Ok(_) => (),
Err(e) => { Err(e) => {
eprintln!("Error: {}", e); eprintln!("Error: {}", e);
@@ -36,19 +51,24 @@ fn main() {
fn print_usage(program: &str) { fn print_usage(program: &str) {
eprintln!( eprintln!(
"Usage: {} <input.gif> <output.gif> <color> [texture]", "Usage: {} <input.gif> <output.gif> <white_color> <black_color> [texture]",
program program
); );
eprintln!(); eprintln!();
eprintln!("Arguments:"); eprintln!("Arguments:");
eprintln!(" <input.gif> Path to input GIF file"); eprintln!(" <input.gif> Path to input GIF file");
eprintln!(" <output.gif> Path to output GIF file"); eprintln!(" <output.gif> Path to output GIF file");
eprintln!(" <color> Target color in hex format (e.g., FF5733 or #FF5733)"); eprintln!(
" <white_color> Target color for white pixels in hex format (e.g., FF5733 or #FF5733)"
);
eprintln!(
" <black_color> Target color for black pixels in hex format (e.g., 000000 or #000000)"
);
eprintln!(" [texture] Optional: 'true' to apply texture/noise (default: false)"); eprintln!(" [texture] Optional: 'true' to apply texture/noise (default: false)");
eprintln!(); eprintln!();
eprintln!("Examples:"); eprintln!("Examples:");
eprintln!(" {} neko.gif neko_red.gif FF0000", program); eprintln!(" {} neko.gif neko_red.gif FF0000 000000", program);
eprintln!(" {} neko.gif neko_blue.gif 0066FF true", program); eprintln!(" {} neko.gif neko_blue.gif 0066FF 330000 true", program);
} }
fn parse_hex_color(hex: &str) -> Result<Rgba<u8>, String> { fn parse_hex_color(hex: &str) -> Result<Rgba<u8>, String> {
@@ -67,7 +87,8 @@ fn parse_hex_color(hex: &str) -> Result<Rgba<u8>, String> {
fn recolor_gif( fn recolor_gif(
input_path: &str, input_path: &str,
output_path: &str, output_path: &str,
target_color: Rgba<u8>, white_color: Rgba<u8>,
black_color: Rgba<u8>,
apply_texture: bool, apply_texture: bool,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
// Open and decode input GIF // Open and decode input GIF
@@ -94,7 +115,7 @@ fn recolor_gif(
.ok_or("Failed to create image from frame")?; .ok_or("Failed to create image from frame")?;
// Recolor the image // Recolor the image
recolor_image(&mut img, target_color, apply_texture); recolor_image(&mut img, white_color, black_color, apply_texture);
// Create output frame with same timing // Create output frame with same timing
let mut pixel_data = img.into_raw(); let mut pixel_data = img.into_raw();
@@ -107,12 +128,23 @@ fn recolor_gif(
Ok(()) Ok(())
} }
fn recolor_image(img: &mut RgbaImage, target_color: Rgba<u8>, apply_texture: bool) { fn recolor_image(
// Generate color palette with variations if texture is enabled img: &mut RgbaImage,
let color_palette = if apply_texture { white_color: Rgba<u8>,
generate_color_palette(target_color, 7) black_color: Rgba<u8>,
apply_texture: bool,
) {
// Generate color palettes with variations if texture is enabled
let white_palette = if apply_texture {
generate_color_palette(white_color, 7)
} else { } else {
vec![target_color] vec![white_color]
};
let black_palette = if apply_texture {
generate_color_palette(black_color, 7)
} else {
vec![black_color]
}; };
for (x, y, pixel) in img.enumerate_pixels_mut() { for (x, y, pixel) in img.enumerate_pixels_mut() {
@@ -128,14 +160,19 @@ fn recolor_image(img: &mut RgbaImage, target_color: Rgba<u8>, apply_texture: boo
// Determine pixel type based on brightness // Determine pixel type based on brightness
if brightness < 64 { if brightness < 64 {
// Very dark pixels (outlines) - keep original // Very dark pixels (outlines) - apply black target color
continue;
} else if brightness > 200 {
// White/light pixels (body) - apply target color
let chosen_color = if apply_texture { let chosen_color = if apply_texture {
pick_color_from_palette(&color_palette, x, y) pick_color_from_palette(&black_palette, x, y)
} else { } else {
target_color black_color
};
*pixel = chosen_color;
} else if brightness > 200 {
// White/light pixels (body) - apply white target color
let chosen_color = if apply_texture {
pick_color_from_palette(&white_palette, x, y)
} else {
white_color
}; };
*pixel = chosen_color; *pixel = chosen_color;
} else { } else {
@@ -145,9 +182,9 @@ fn recolor_image(img: &mut RgbaImage, target_color: Rgba<u8>, apply_texture: boo
let blend_factor = blend_factor.clamp(0.0, 1.0); let blend_factor = blend_factor.clamp(0.0, 1.0);
let chosen_color = if apply_texture { let chosen_color = if apply_texture {
pick_color_from_palette(&color_palette, x, y) pick_color_from_palette(&white_palette, x, y)
} else { } else {
target_color white_color
}; };
let new_r = let new_r =