use clap::Parser; use libraw_sys::*; use std::ffi::CString; use std::os::raw::c_int; use std::path::Path; /// Photo Editor - A Rust-based photo editor with RAW image processing capabilities using libraw #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { /// Input raw image file to process #[arg(short, long)] input: String, /// Output processed image file #[arg(short, long)] output: String, /// Exposure adjustment (-2.0 to 2.0) #[arg(long, default_value = "0.0")] exposure: f32, /// Contrast adjustment (-100 to 100) #[arg(long, default_value = "0")] contrast: i32, /// Highlights adjustment (-100 to 100) #[arg(long, default_value = "0")] highlights: i32, /// Shadows adjustment (-100 to 100) #[arg(long, default_value = "0")] shadows: i32, /// Whites adjustment (-100 to 100) #[arg(long, default_value = "0")] whites: i32, /// Blacks adjustment (-100 to 100) #[arg(long, default_value = "0")] blacks: i32, /// Clarity adjustment (-100 to 100) #[arg(long, default_value = "0")] clarity: i32, /// Vibrance adjustment (-100 to 100) #[arg(long, default_value = "0")] vibrance: i32, /// Saturation adjustment (-100 to 100) #[arg(long, default_value = "0")] saturation: i32, /// Temperature adjustment (-100 to 100) #[arg(long, default_value = "0")] temperature: i32, /// Tint adjustment (-100 to 100) #[arg(long, default_value = "0")] tint: i32, } fn main() -> Result<(), Box> { let args = Args::parse(); println!("Photo Editor v0.1"); println!("=================="); println!("Input file: {}", args.input); println!("Output file: {}", args.output); println!("Exposure: {}", args.exposure); println!("Contrast: {}", args.contrast); println!("Highlights: {}", args.highlights); println!("Shadows: {}", args.shadows); println!("Whites: {}", args.whites); println!("Blacks: {}", args.blacks); println!("Clarity: {}", args.clarity); println!("Vibrance: {}", args.vibrance); println!("Saturation: {}", args.saturation); println!("Temperature: {}", args.temperature); println!("Tint: {}", args.tint); // Check if input file exists if !Path::new(&args.input).exists() { eprintln!("Error: Input file '{}' does not exist.", args.input); return Err("Input file not found".into()); } println!("\n[INFO] Attempting RAW image processing with libraw integration..."); // Initialize libraw let lr = unsafe { libraw_init(LIBRAW_OPTIONS_NONE) }; if lr.is_null() { eprintln!("Error: Failed to initialize libraw"); return Err("Failed to initialize libraw".into()); } // Open the RAW file let input_cstr = CString::new(args.input.clone()).unwrap(); let ret = unsafe { libraw_open_file(lr, input_cstr.as_ptr()) }; if ret != LIBRAW_SUCCESS { eprintln!("Error: Failed to open file. Error code: {}", ret); unsafe { libraw_close(lr) }; return Err("Failed to open file".into()); } println!("[INFO] File opened successfully"); // Process the RAW data let ret = unsafe { libraw_unpack(lr) }; if ret != LIBRAW_SUCCESS { eprintln!("Error: Failed to unpack RAW data. Error code: {}", ret); unsafe { libraw_close(lr) }; return Err("Failed to unpack RAW data".into()); } println!("[INFO] RAW data unpacked successfully"); // Adjust image parameters based on user input apply_image_parameters(lr, &args); println!("[INFO] Image parameters applied"); // Process the image let ret = unsafe { libraw_dcraw_process(lr) }; if ret != LIBRAW_SUCCESS { eprintln!("Error: Failed to process image. Error code: {}", ret); unsafe { libraw_close(lr) }; return Err("Failed to process image".into()); } println!("[INFO] Image processed successfully"); // Determine output format from extension let ext = Path::new(&args.output) .extension() .and_then(|s| s.to_str()) .unwrap_or("jpg") .to_lowercase(); let ret = match ext.as_str() { "jpg" | "jpeg" => { // For JPEG output, we'll write directly to file let output_cstr = CString::new(args.output.clone()).unwrap(); unsafe { libraw_dcraw_ppm_tiff_writer(lr, output_cstr.as_ptr()) } } _ => { // For other formats, attempt to convert let output_cstr = CString::new(args.output.clone()).unwrap(); unsafe { libraw_dcraw_ppm_tiff_writer(lr, output_cstr.as_ptr()) } } }; if ret != LIBRAW_SUCCESS { eprintln!("Error: Failed to write output file. Error code: {}", ret); unsafe { libraw_close(lr) }; return Err("Failed to write output file".into()); } println!("[INFO] Processed image written to disk"); // Clean up unsafe { libraw_close(lr); } println!("\n[SUCCESS] Photo editing completed successfully with libraw integration!"); println!( "[NOTICE] The edited image has been saved as: {}", args.output ); Ok(()) } fn apply_image_parameters(lr: *mut libraw_data_t, args: &Args) { unsafe { // Set various processing parameters based on user input // Apply exposure adjustment if args.exposure != 0.0 { libraw_set_bright(lr, args.exposure); } // Apply contrast (using highlight parameters for demonstration) if args.contrast != 0 { let value = args.contrast.min(100).max(-100) as c_int; libraw_set_highlight(lr, value); } // Apply highlights adjustment if args.highlights != 0 { let value = args.highlights.min(100).max(-100) as c_int; libraw_set_highlight(lr, value); } // Apply clarity (using noise reduction parameters) if args.clarity != 0 { let value = args.clarity.min(100).max(-100) as c_int; libraw_set_fbdd_noiserd(lr, value); } // Apply saturation if args.saturation != 0 { let value = 1.0 + args.saturation.min(100).max(-100) as f32 / 100.0; libraw_set_gamma(lr, 0, value); // Apply gamma adjustment to simulate saturation } // Set output color space (sRGB in this case) libraw_set_output_color(lr, 1); // 1 = sRGB // Set output bits per sample libraw_set_output_bps(lr, 8); } } #[cfg(test)] mod tests { use super::*; #[test] fn test_args_parsing() { let args = Args::parse_from([ "photo_editor", "--input", "test.raw", "--output", "output.jpg", ]); assert_eq!(args.input, "test.raw"); assert_eq!(args.output, "output.jpg"); } #[test] fn test_args_with_positive_values() { let args = Args::parse_from([ "photo_editor", "--input", "input.raw", "--output", "output.jpg", "--exposure", "0.5", "--contrast", "20", "--highlights", "10", ]); assert_eq!(args.input, "input.raw"); assert_eq!(args.output, "output.jpg"); assert_eq!(args.exposure, 0.5); assert_eq!(args.contrast, 20); assert_eq!(args.highlights, 10); } }