use image::{DynamicImage, ImageBuffer, Rgb, Rgba};
use std::env;
use std::time::Instant;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!(
"Usage: {} <cube-file> [input-image] [output-image]",
args[0]
);
println!("\nExamples:");
println!(
" {} lut.cube # Create test image and apply LUT",
args[0]
);
println!(
" {} lut.cube input.jpg out.jpg # Apply LUT to image file",
args[0]
);
println!("\nThis example demonstrates in-place (mut) processing:");
println!(" - Zero memory allocation during processing");
println!(" - Direct modification of image buffer");
println!(" - Preserves alpha channel in RGBA images");
return Ok(());
}
let cube_path = &args[1];
println!("Loading CUBE LUT from: {}", cube_path);
let lut = wagahai_lut::CubeParser::from_file(cube_path)?;
println!("\nLUT Information:");
if let Some(ref title) = lut.title {
println!(" Title: {}", title);
}
println!(" Type: {:?}", lut.get_lut_type());
if args.len() >= 4 {
let input_path = &args[2];
let output_path = &args[3];
println!("\nApplying LUT to image: {}", input_path);
let load_start = Instant::now();
let image = image::open(input_path)?;
let load_duration = load_start.elapsed();
println!(" Image size: {}x{}", image.width(), image.height());
println!(" Image load time: {:?}", load_duration);
let apply_start = Instant::now();
let processed = match image {
DynamicImage::ImageRgb8(img) => {
println!(" Converting RGB with in-place processing...");
let mut img = img;
wagahai_lut::lut::apply_rgb_mut(&lut, &mut img);
DynamicImage::ImageRgb8(img)
}
DynamicImage::ImageRgba8(img) => {
println!(" Converting RGBA with in-place processing (alpha preserved)...");
let mut img = img;
wagahai_lut::lut::apply_rgba_mut(&lut, &mut img);
DynamicImage::ImageRgba8(img)
}
DynamicImage::ImageRgb16(img) => {
println!(" Converting RGB16 (downsampling to RGB8)...");
let img = DynamicImage::ImageRgb16(img).to_rgb8();
let mut img = img;
wagahai_lut::lut::apply_rgb_mut(&lut, &mut img);
DynamicImage::ImageRgb8(img)
}
DynamicImage::ImageRgba16(img) => {
println!(" Converting RGBA16 (downsampling to RGBA8)...");
let img = DynamicImage::ImageRgba16(img).to_rgba8();
let mut img = img;
wagahai_lut::lut::apply_rgba_mut(&lut, &mut img);
DynamicImage::ImageRgba8(img)
}
DynamicImage::ImageLuma8(img) => {
println!(" Converting grayscale (duplicating to RGB)...");
let img = DynamicImage::ImageLuma8(img).to_rgb8();
let mut img = img;
wagahai_lut::lut::apply_rgb_mut(&lut, &mut img);
DynamicImage::ImageRgb8(img)
}
DynamicImage::ImageLumaA8(img) => {
println!(" Converting grayscale+alpha (converting to RGBA)...");
let img = DynamicImage::ImageLumaA8(img).to_rgba8();
let mut img = img;
wagahai_lut::lut::apply_rgba_mut(&lut, &mut img);
DynamicImage::ImageRgba8(img)
}
_ => {
println!(" Converting to RGB8...");
let img = image.to_rgb8();
let mut img = img;
wagahai_lut::lut::apply_rgb_mut(&lut, &mut img);
DynamicImage::ImageRgb8(img)
}
};
let apply_duration = apply_start.elapsed();
println!(" LUT apply time (in-place): {:?}", apply_duration);
let save_start = Instant::now();
processed.save(output_path)?;
let save_duration = save_start.elapsed();
println!(" Image save time: {:?}", save_duration);
println!(
" Total time: {:?}",
load_duration + apply_duration + save_duration
);
println!(" Saved to: {}", output_path);
} else {
println!("\nCreating test images...");
println!("\n1. RGB Image (800x600):");
let create_start = Instant::now();
let mut rgb_image = create_test_rgb_image(800, 600);
let create_duration = create_start.elapsed();
println!(
" Created test image with color gradients: {:?}",
create_duration
);
let apply_start = Instant::now();
wagahai_lut::lut::apply_rgb_mut(&lut, &mut rgb_image);
let apply_duration = apply_start.elapsed();
println!(
" Applied LUT in-place (zero allocation): {:?}",
apply_duration
);
let save_start = Instant::now();
rgb_image.save("test_output_rgb.png")?;
let save_duration = save_start.elapsed();
println!(" Saved to: test_output_rgb.png: {:?}", save_duration);
println!("\n2. RGBA Image (800x600):");
let create_start = Instant::now();
let mut rgba_image = create_test_rgba_image(800, 600);
let create_duration = create_start.elapsed();
println!(
" Created test image with color gradients and alpha: {:?}",
create_duration
);
let apply_start = Instant::now();
wagahai_lut::lut::apply_rgba_mut(&lut, &mut rgba_image);
let apply_duration = apply_start.elapsed();
println!(
" Applied LUT in-place (alpha preserved, zero allocation): {:?}",
apply_duration
);
let save_start = Instant::now();
rgba_image.save("test_output_rgba.png")?;
let save_duration = save_start.elapsed();
println!(" Saved to: test_output_rgba.png: {:?}", save_duration);
println!("\n3. Color Value Tests:");
let colors = [
("Red ", [1.0, 0.0, 0.0]),
("Green", [0.0, 1.0, 0.0]),
("Blue ", [0.0, 0.0, 1.0]),
("White", [1.0, 1.0, 1.0]),
("Black", [0.0, 0.0, 0.0]),
];
for (name, color) in colors {
let result = lut.apply_to_color(color)?;
println!(
" {:>5} [{:.1}, {:.1}, {:.1}] -> [{:.4}, {:.4}, {:.4}]",
name, color[0], color[1], color[2], result[0], result[1], result[2]
);
}
println!("\nTest images saved successfully!");
println!("\nKey Features of In-Place Processing:");
println!(" ✓ Zero memory allocation during processing");
println!(" ✓ Direct modification of image buffer");
println!(" ✓ Better memory efficiency for large images");
println!(" ✓ Alpha channel preserved in RGBA images");
println!(" ✓ Auto-detects 1D/3D LUT type");
}
Ok(())
}
fn create_test_rgb_image(width: u32, height: u32) -> ImageBuffer<Rgb<u8>, Vec<u8>> {
let mut img = ImageBuffer::new(width, height);
for y in 0..height {
for x in 0..width {
let r = (x as f32) / (width as f32 - 1.0);
let g = (y as f32) / (height as f32 - 1.0);
let b = 1.0 - r;
let pixel = Rgb([(r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8]);
img.put_pixel(x, y, pixel);
}
}
img
}
fn create_test_rgba_image(width: u32, height: u32) -> ImageBuffer<Rgba<u8>, Vec<u8>> {
let mut img = ImageBuffer::new(width, height);
for y in 0..height {
for x in 0..width {
let r = (x as f32) / (width as f32 - 1.0);
let g = (y as f32) / (height as f32 - 1.0);
let b = 1.0 - r;
let a = ((x + y) as f32) / ((width + height) as f32);
let pixel = Rgba([
(r * 255.0) as u8,
(g * 255.0) as u8,
(b * 255.0) as u8,
(a * 255.0) as u8,
]);
img.put_pixel(x, y, pixel);
}
}
img
}